| @@ -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: ( | |||