@@ -7,10 +7,10 @@ | |||
``` | |||
## 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 | |||
- Start the application by the following command: | |||
@@ -35,8 +35,8 @@ export default function ClientTable({recordList}) { | |||
navigate('/client/maintain/'+ id); | |||
}; | |||
const handlePDFClick = () => () => { | |||
navigate('/pdf'); | |||
const handlePdfClick = (id) => () => { | |||
navigate(`/pdf/${id}`); | |||
}; | |||
const columns = [ | |||
@@ -63,7 +63,7 @@ export default function ClientTable({recordList}) { | |||
icon={<PictureAsPdfIcon sx={{fontSize: 25}}/>} | |||
label="Edit PDF" | |||
className="textPrimary" | |||
onClick={handlePDFClick()} | |||
onClick={handlePdfClick(id)} | |||
color="edit" | |||
// disabled={'true'} | |||
// disabled={!ability.can('VIEW','DASHBOARD')} | |||
@@ -1,61 +1,107 @@ | |||
import React, { useEffect, useRef, useState } from 'react'; | |||
import { Button, Grid } from '@mui/material'; | |||
import { GeneralConfirmWindow } from "../../utils/CommonFunction"; | |||
import { GeneralConfirmWindow, notifySaveSuccess } from "../../../utils/CommonFunction"; | |||
import axios from 'axios'; | |||
import {apiPath} from "../../auth/utils"; | |||
import {apiPath} from "../../../auth/utils"; | |||
import { | |||
GET_PDF_TEMPLATE_PATH, | |||
GET_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 {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 PSPDFKit from 'pspdfkit'; | |||
import { CollectionsBookmarkRounded } from '../../../../node_modules/@mui/icons-material/index'; | |||
function PDF() { | |||
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 [viewerLoaded, setViewerLoaded] = useState(false); | |||
const [template,setTemplate] = useState(); | |||
const [viewInstance,setViewInstance] = useState(); | |||
const [pdfBytes, setPdfBytes] = useState(); | |||
const [record, setRecord] = useState(); | |||
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 { | |||
//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); | |||
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 | |||
container: viewerRef.current, | |||
// container: '#viewer', | |||
@@ -76,6 +122,13 @@ function PDF() { | |||
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; | |||
@@ -95,32 +148,55 @@ function PDF() { | |||
}; | |||
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(() => { | |||
loadPdfViewer(); | |||
}, [viewerRef, pdfLoaded]); | |||
}, [viewerRef.current, pdfLoaded]); | |||
const handleSavePdf = async () => { | |||
if (viewInstance.current) {console.log("tetes"); | |||
if (viewInstance) { | |||
try { | |||
// 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' }); | |||
// 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 | |||
} | |||
}, | |||
}); | |||
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) { | |||
console.error('Error saving PDF:', error); | |||
alert('Failed to save PDF.'); | |||
@@ -146,8 +222,11 @@ function PDF() { | |||
setIsWindowOpen(true); | |||
}; | |||
const handleBack = () => { | |||
navigate('/client'); | |||
const handleBack = async () => { | |||
if (viewerLoaded) { | |||
await PSPDFKit.unload(viewerRef.current); | |||
} | |||
navigate(`/pdf/${record.clientId}`); | |||
}; | |||
return ( | |||
@@ -162,7 +241,7 @@ function PDF() { | |||
type="submit" | |||
color="save" | |||
disabled={!viewerLoaded} | |||
// onClick={handleSavePdf} | |||
onClick={handleSavePdf} | |||
> | |||
Save | |||
</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 | |||
const ClientSearchPage = Loadable(lazy( () => import('pages/client/ClientSearchPage'))); | |||
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 ||============================== // | |||
@@ -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 | |||
//const DashboardDefault = Loadable(lazy(() => import('pages/dashboard'))); | |||
const LIONERDashboard = Loadable(lazy(() => import('pages/lionerdashboard'))); | |||
const PDF = Loadable(lazy(() => import('pages/pdf'))); | |||
const ReminderPage = Loadable(lazy(() => import('pages/lionerReminderPage'))); | |||
const TemplateSearchPage = Loadable(lazy(() => import('pages/lionerSearchPanel'))); | |||
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', | |||
// element: ( | |||