| @@ -13,7 +13,8 @@ import { | |||
| MenuUnfoldOutlined, | |||
| FileSearchOutlined, | |||
| MailOutlined, | |||
| ApartmentOutlined | |||
| ApartmentOutlined, | |||
| FilePdfOutlined | |||
| } from '@ant-design/icons'; | |||
| // icons | |||
| @@ -31,7 +32,8 @@ const icons = { | |||
| MenuUnfoldOutlined, | |||
| FileSearchOutlined, | |||
| MailOutlined, | |||
| ApartmentOutlined | |||
| ApartmentOutlined, | |||
| FilePdfOutlined | |||
| }; | |||
| // ==============================|| MENU ITEMS - EXTRA PAGES ||============================== // | |||
| @@ -141,6 +143,15 @@ const setting = { | |||
| breadcrumbs: false, | |||
| ability:['VIEW','USER'] | |||
| }, | |||
| { | |||
| id: 'formSigPage', | |||
| title: 'Form Sig Page', | |||
| type: 'item', | |||
| url: '/formSigPage', | |||
| icon: icons.FilePdfOutlined, | |||
| breadcrumbs: false, | |||
| ability:['MANAGE','SYSTEM_CONFIGURATION'] | |||
| }, | |||
| // { | |||
| // id: 'passwordPolicy', | |||
| // title: 'Password Policy', | |||
| @@ -0,0 +1,84 @@ | |||
| import { | |||
| Button, | |||
| Grid, | |||
| InputLabel, | |||
| MenuItem, | |||
| TextField, | |||
| } from '@mui/material'; | |||
| import MainCard from "../../components/MainCard"; | |||
| import { useForm } from "react-hook-form"; | |||
| import * as React from "react"; | |||
| import { ThemeProvider } from "@emotion/react"; | |||
| import { LIONER_BUTTON_THEME } from "../../themes/colorConst"; | |||
| import { CARD_MAX_WIDTH } from "../../themes/themeConst"; | |||
| const FORM_CODES = ['IDA', 'FNA', 'HSBCFIN', 'HSBCA31', 'MLB03S', 'MLFNA_EN', 'MLFNA_CHI', 'SLFNA_EN', 'SLFNA_CHI', 'SLAPP', 'SLGII']; | |||
| const SIG_TYPES = ['upload1', 'upload2']; | |||
| const FormSigPageSearchForm = ({ applySearch }) => { | |||
| const { reset, register, handleSubmit } = useForm(); | |||
| const onSubmit = (data) => { | |||
| applySearch(data); | |||
| }; | |||
| return ( | |||
| <MainCard | |||
| xs={12} | |||
| md={12} | |||
| lg={12} | |||
| content={false} | |||
| sx={{ width: CARD_MAX_WIDTH }} | |||
| > | |||
| <form onSubmit={handleSubmit(onSubmit)}> | |||
| <Grid sx={{ mb: 2 }} /> | |||
| <Grid container alignItems="center" spacing={2}> | |||
| <Grid item xs={12} sm={6} md={4} lg={3} sx={{ ml: 3, mr: 3 }}> | |||
| <InputLabel>Form Code</InputLabel> | |||
| <TextField | |||
| fullWidth | |||
| select | |||
| {...register("formCode")} | |||
| id="formCode" | |||
| > | |||
| <MenuItem value="">All</MenuItem> | |||
| {FORM_CODES.map((c) => ( | |||
| <MenuItem key={c} value={c}>{c}</MenuItem> | |||
| ))} | |||
| </TextField> | |||
| </Grid> | |||
| <Grid item xs={12} sm={6} md={4} lg={3}> | |||
| <InputLabel>Sig Type</InputLabel> | |||
| <TextField | |||
| fullWidth | |||
| select | |||
| {...register("sigType")} | |||
| id="sigType" | |||
| > | |||
| <MenuItem value="">All</MenuItem> | |||
| {SIG_TYPES.map((t) => ( | |||
| <MenuItem key={t} value={t}>{t}</MenuItem> | |||
| ))} | |||
| </TextField> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container maxWidth justifyContent="flex-start" sx={{ mt: 2, mb: 2 }}> | |||
| <ThemeProvider theme={LIONER_BUTTON_THEME}> | |||
| <Grid item sx={{ ml: 3, mr: 1.5 }}> | |||
| <Button variant="contained" type="submit" color="save"> | |||
| Search | |||
| </Button> | |||
| </Grid> | |||
| <Grid item> | |||
| <Button variant="outlined" type="button" onClick={() => { reset(); applySearch({}); }}> | |||
| Reset | |||
| </Button> | |||
| </Grid> | |||
| </ThemeProvider> | |||
| </Grid> | |||
| </form> | |||
| </MainCard> | |||
| ); | |||
| }; | |||
| export default FormSigPageSearchForm; | |||
| @@ -0,0 +1,325 @@ | |||
| import * as React from 'react'; | |||
| import { | |||
| Button, | |||
| } from '@mui/material'; | |||
| import { | |||
| DataGrid, | |||
| GridActionsCellItem, | |||
| GridRowEditStopReasons, | |||
| GridRowModes, | |||
| GridToolbarContainer | |||
| } from "@mui/x-data-grid"; | |||
| import { DatePicker } from '@mui/x-date-pickers/DatePicker'; | |||
| import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; | |||
| import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; | |||
| import dayjs from 'dayjs'; | |||
| import EditIcon from '@mui/icons-material/Edit'; | |||
| import DeleteIcon from '@mui/icons-material/DeleteOutlined'; | |||
| import SaveIcon from '@mui/icons-material/Save'; | |||
| import CancelIcon from '@mui/icons-material/Close'; | |||
| import AddIcon from '@mui/icons-material/Add'; | |||
| import axios from "axios"; | |||
| import { apiPath } from "../../auth/utils"; | |||
| import { | |||
| GET_FORM_SIG_PAGE_LIST, | |||
| UPDATE_FORM_SIG_PAGE_PATH, | |||
| } from "../../utils/ApiPathConst"; | |||
| import { | |||
| CustomNoRowsOverlay, | |||
| GeneralConfirmWindow, | |||
| notifyDeleteError, | |||
| notifyDeleteSuccess, | |||
| notifySaveSuccess, | |||
| removeObjectWithId | |||
| } from "../../utils/CommonFunction"; | |||
| import UploadContext from "../../components/UploadProvider"; | |||
| import { LIONER_BUTTON_THEME } from "../../themes/colorConst"; | |||
| import { ThemeProvider } from "@emotion/react"; | |||
| const FORM_CODES = ['IDA', 'FNA', 'HSBCFIN', 'HSBCA31', 'MLB03S', 'MLFNA_EN', 'MLFNA_CHI', 'SLFNA_EN', 'SLFNA_CHI', 'SLAPP', 'SLGII']; | |||
| const SIG_TYPES = ['upload1', 'upload2']; | |||
| const ACTIONS = ['REPLACE', 'SKIP_AND_APPEND']; | |||
| let newRowId = -1; | |||
| function normalizeStartDate(value) { | |||
| if (value == null || value === '') return ''; | |||
| if (dayjs.isDayjs(value)) return value.format('YYYY-MM-DD'); | |||
| const d = dayjs(value); | |||
| return d.isValid() ? d.format('YYYY-MM-DD') : String(value); | |||
| } | |||
| function toDayjsOrNull(value) { | |||
| if (value == null || value === '') return null; | |||
| if (dayjs.isDayjs(value)) return value; | |||
| if (typeof value === 'string') { | |||
| const d = dayjs(value); | |||
| return d.isValid() ? d : null; | |||
| } | |||
| if (Array.isArray(value) && value.length >= 3) { | |||
| const y = value[0]; | |||
| const m = String(value[1]).padStart(2, '0'); | |||
| const d = dayjs(`${y}-${m}-${String(value[2]).padStart(2, '0')}`); | |||
| return d.isValid() ? d : null; | |||
| } | |||
| if (typeof value === 'object' && value.year != null && value.month != null && value.day != null) { | |||
| const d = dayjs(`${value.year}-${String(value.month).padStart(2, '0')}-${String(value.day).padStart(2, '0')}`); | |||
| return d.isValid() ? d : null; | |||
| } | |||
| const d = dayjs(value); | |||
| return d.isValid() ? d : null; | |||
| } | |||
| function formatStartDate(value) { | |||
| const d = toDayjsOrNull(value); | |||
| return d ? d.format('YYYY-MM-DD') : ''; | |||
| } | |||
| function EditToolbar({ setRows, setRowModesModel }) { | |||
| const handleClick = () => { | |||
| const id = newRowId--; | |||
| setRows((oldRows) => [ | |||
| { | |||
| id, | |||
| formCode: 'IDA', | |||
| startDate: '2000-01-01', | |||
| sigType: 'upload1', | |||
| pageFrom: 1, | |||
| pageTo: 1, | |||
| action: 'REPLACE', | |||
| isNew: true, | |||
| }, | |||
| ...oldRows, | |||
| ]); | |||
| setRowModesModel((oldModel) => ({ | |||
| ...oldModel, | |||
| [id]: { mode: GridRowModes.Edit, fieldToFocus: 'formCode' }, | |||
| })); | |||
| }; | |||
| return ( | |||
| <GridToolbarContainer sx={{ ml: 1 }}> | |||
| <Button color="primary" startIcon={<AddIcon />} onClick={handleClick}> | |||
| Add Form Sig Page | |||
| </Button> | |||
| </GridToolbarContainer> | |||
| ); | |||
| } | |||
| export default function FormSigPageTable({ recordList }) { | |||
| const [rows, setRows] = React.useState([]); | |||
| const [rowModesModel, setRowModesModel] = React.useState({}); | |||
| const { setIsUploading } = React.useContext(UploadContext); | |||
| const [isWindowOpen, setIsWindowOpen] = React.useState(false); | |||
| const [selectedId, setSelectedId] = React.useState(null); | |||
| const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize: 10 }); | |||
| React.useEffect(() => { | |||
| setPaginationModel({ page: 0, pageSize: 10 }); | |||
| setRows(recordList || []); | |||
| }, [recordList]); | |||
| const handleClose = () => setIsWindowOpen(false); | |||
| const handleDeleteClick = (id) => () => { | |||
| setIsWindowOpen(true); | |||
| setSelectedId(id); | |||
| }; | |||
| const updateData = () => { | |||
| setIsUploading(true); | |||
| axios | |||
| .delete(`${apiPath}${GET_FORM_SIG_PAGE_LIST}/${selectedId}`) | |||
| .then((response) => { | |||
| if (response.status === 204) { | |||
| notifyDeleteSuccess(); | |||
| setRows((prev) => removeObjectWithId(prev, selectedId)); | |||
| setIsWindowOpen(false); | |||
| } | |||
| setIsUploading(false); | |||
| }) | |||
| .catch((err) => { | |||
| console.error(err); | |||
| notifyDeleteError(err?.response?.data?.message || 'Delete failed'); | |||
| setIsUploading(false); | |||
| }); | |||
| }; | |||
| const handleRowEditStop = (params, event) => { | |||
| if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||
| event.defaultMuiPrevented = true; | |||
| } | |||
| }; | |||
| const handleEditClick = (id) => () => { | |||
| setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.Edit, fieldToFocus: 'formCode' } })); | |||
| }; | |||
| const handleSaveClick = (id) => () => { | |||
| setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View } })); | |||
| }; | |||
| const handleCancelClick = (id) => () => { | |||
| setRowModesModel((prev => { | |||
| const next = { ...prev, [id]: { mode: GridRowModes.View, ignoreModifications: true } }; | |||
| return next; | |||
| })); | |||
| const editedRow = rows.find((r) => r.id === id); | |||
| if (editedRow?.isNew) { | |||
| setRows((prev) => prev.filter((r) => r.id !== id)); | |||
| } | |||
| }; | |||
| const processRowUpdate = (newRow) => { | |||
| const isCreate = newRow.isNew === true || newRow.id == null || Number(newRow.id) <= 0; | |||
| const payload = { | |||
| formCode: newRow.formCode, | |||
| startDate: normalizeStartDate(newRow.startDate), | |||
| sigType: newRow.sigType, | |||
| pageFrom: Number(newRow.pageFrom), | |||
| pageTo: Number(newRow.pageTo), | |||
| action: newRow.action, | |||
| }; | |||
| if (!isCreate) payload.id = Number(newRow.id); | |||
| return new Promise((resolve, reject) => { | |||
| if (!payload.formCode || !payload.startDate || !payload.sigType || | |||
| payload.pageFrom == null || payload.pageTo == null || !payload.action) { | |||
| reject(new Error('All fields are required')); | |||
| return; | |||
| } | |||
| if (payload.pageFrom > payload.pageTo) { | |||
| reject(new Error('pageFrom must be <= pageTo')); | |||
| return; | |||
| } | |||
| setIsUploading(true); | |||
| axios | |||
| .post(`${apiPath}${UPDATE_FORM_SIG_PAGE_PATH}`, payload) | |||
| .then((res) => { | |||
| const savedId = res.data?.id; | |||
| const updatedRow = { ...newRow, id: savedId != null ? savedId : newRow.id, isNew: false }; | |||
| setRows((prev) => prev.map((r) => (r.id === newRow.id ? updatedRow : r))); | |||
| notifySaveSuccess(); | |||
| setIsUploading(false); | |||
| resolve(updatedRow); | |||
| }) | |||
| .catch((err) => { | |||
| setIsUploading(false); | |||
| notifyDeleteError(err?.response?.data?.message || 'Save failed'); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| }; | |||
| const columns = [ | |||
| { | |||
| field: 'actions', | |||
| type: 'actions', | |||
| headerName: 'Actions', | |||
| width: 120, | |||
| getActions: ({ id }) => { | |||
| const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
| if (isInEditMode) { | |||
| return [ | |||
| <ThemeProvider key="cancel" theme={LIONER_BUTTON_THEME}> | |||
| <GridActionsCellItem icon={<CancelIcon />} label="Cancel" onClick={handleCancelClick(id)} color="delete" />, | |||
| </ThemeProvider>, | |||
| <ThemeProvider key="save" theme={LIONER_BUTTON_THEME}> | |||
| <GridActionsCellItem icon={<SaveIcon />} label="Save" onClick={handleSaveClick(id)} color="save" />, | |||
| </ThemeProvider>, | |||
| ]; | |||
| } | |||
| return [ | |||
| <ThemeProvider key="delete" theme={LIONER_BUTTON_THEME}> | |||
| <GridActionsCellItem icon={<DeleteIcon />} label="Delete" onClick={handleDeleteClick(id)} color="delete" />, | |||
| </ThemeProvider>, | |||
| <ThemeProvider key="edit" theme={LIONER_BUTTON_THEME}> | |||
| <GridActionsCellItem icon={<EditIcon />} label="Edit" onClick={handleEditClick(id)} color="edit" />, | |||
| </ThemeProvider>, | |||
| ]; | |||
| }, | |||
| }, | |||
| { | |||
| field: 'formCode', | |||
| headerName: 'Form Code', | |||
| width: 120, | |||
| editable: true, | |||
| type: 'singleSelect', | |||
| valueOptions: FORM_CODES, | |||
| }, | |||
| { | |||
| field: 'startDate', | |||
| headerName: 'Start Date', | |||
| width: 140, | |||
| editable: true, | |||
| valueGetter: (params) => formatStartDate(params.row?.startDate), | |||
| renderCell: (params) => formatStartDate(params.row?.startDate), | |||
| renderEditCell: (params) => ( | |||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | |||
| <DatePicker | |||
| value={toDayjsOrNull(params.row?.startDate)} | |||
| onChange={(d) => | |||
| params.api.setEditCellValue({ | |||
| id: params.id, | |||
| field: params.field, | |||
| value: d ? d.format('YYYY-MM-DD') : '', | |||
| }) | |||
| } | |||
| slotProps={{ textField: { size: 'small', fullWidth: true } }} | |||
| /> | |||
| </LocalizationProvider> | |||
| ), | |||
| }, | |||
| { | |||
| field: 'sigType', | |||
| headerName: 'Sig Type', | |||
| width: 90, | |||
| editable: true, | |||
| type: 'singleSelect', | |||
| valueOptions: SIG_TYPES, | |||
| }, | |||
| { field: 'pageFrom', headerName: 'Page From', type: 'number', width: 95, editable: true }, | |||
| { field: 'pageTo', headerName: 'Page To', type: 'number', width: 95, editable: true }, | |||
| { | |||
| field: 'action', | |||
| headerName: 'Action', | |||
| width: 140, | |||
| editable: true, | |||
| type: 'singleSelect', | |||
| valueOptions: ACTIONS, | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div style={{ width: '100%' }}> | |||
| <DataGrid | |||
| rows={rows} | |||
| columns={columns} | |||
| columnHeaderHeight={45} | |||
| editMode="row" | |||
| rowModesModel={rowModesModel} | |||
| onRowModesModelChange={setRowModesModel} | |||
| onRowEditStop={handleRowEditStop} | |||
| processRowUpdate={processRowUpdate} | |||
| onProcessRowUpdateError={(err) => notifyDeleteError(err?.message || 'Update failed')} | |||
| getRowHeight={() => 'auto'} | |||
| paginationModel={paginationModel} | |||
| onPaginationModelChange={setPaginationModel} | |||
| pageSizeOptions={[10, 25, 50]} | |||
| slots={{ | |||
| toolbar: EditToolbar, | |||
| noRowsOverlay: () => CustomNoRowsOverlay(), | |||
| }} | |||
| slotProps={{ toolbar: { setRows, setRowModesModel } }} | |||
| autoHeight | |||
| /> | |||
| <GeneralConfirmWindow | |||
| isWindowOpen={isWindowOpen} | |||
| title="Delete Form Sig Page" | |||
| content="Are you sure you want to delete this record?" | |||
| onNormalClose={handleClose} | |||
| onConfirmClose={updateData} | |||
| /> | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,78 @@ | |||
| import { | |||
| Box, | |||
| Grid, | |||
| Typography | |||
| } from '@mui/material'; | |||
| import MainCard from "../../components/MainCard"; | |||
| import FormSigPageSearchForm from "./FormSigPageSearchForm"; | |||
| import FormSigPageTable from "./FormSigPageTable"; | |||
| import { useEffect, useState } from "react"; | |||
| import * as React from "react"; | |||
| import axios from "axios"; | |||
| import { apiPath } from "../../auth/utils"; | |||
| import { GET_FORM_SIG_PAGE_LIST } from "../../utils/ApiPathConst"; | |||
| import LoadingComponent from "../extra-pages/LoadingComponent"; | |||
| import { LIONER_FORM_THEME, CARD_MAX_WIDTH } from "../../themes/themeConst"; | |||
| import { ThemeProvider } from "@emotion/react"; | |||
| const FormSigPageSearchPanel = () => { | |||
| const [record, setRecord] = useState([]); | |||
| const [searchCriteria, setSearchCriteria] = useState({}); | |||
| const [onReady, setOnReady] = useState(false); | |||
| const fetchList = () => { | |||
| const params = {}; | |||
| if (searchCriteria.formCode) params.formCode = searchCriteria.formCode; | |||
| if (searchCriteria.sigType) params.sigType = searchCriteria.sigType; | |||
| axios | |||
| .get(`${apiPath}${GET_FORM_SIG_PAGE_LIST}`, { params }) | |||
| .then((response) => { | |||
| if (response.status === 200 && response.data?.records) { | |||
| setRecord(response.data.records); | |||
| } | |||
| }) | |||
| .catch((err) => { | |||
| console.error(err); | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| fetchList(); | |||
| }, [searchCriteria]); | |||
| useEffect(() => { | |||
| setOnReady(true); | |||
| }, [record]); | |||
| const applySearch = (input) => { | |||
| setSearchCriteria(input || {}); | |||
| }; | |||
| if (!onReady) return <LoadingComponent />; | |||
| return ( | |||
| <Grid container rowSpacing={3} columnSpacing={2.75}> | |||
| <ThemeProvider theme={LIONER_FORM_THEME}> | |||
| <Grid item xs={12} md={12} lg={12} sx={{ mt: -1 }}> | |||
| <Grid container justifyContent="flex-start" alignItems="center"> | |||
| <Grid item xs={3} sx={{ mb: -2.25 }}> | |||
| <Box sx={{ display: 'flex', alignItems: 'center' }}> | |||
| <Typography variant="h5">Form Signature Page Config</Typography> | |||
| </Box> | |||
| </Grid> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid item xs={12} md={12} lg={12}> | |||
| <FormSigPageSearchForm applySearch={applySearch} /> | |||
| </Grid> | |||
| <Grid item xs={12} md={12} lg={12} sx={{ mt: { lg: -1.5 } }}> | |||
| <MainCard elevation={0} content={false} sx={{ width: CARD_MAX_WIDTH }}> | |||
| <FormSigPageTable recordList={record} /> | |||
| </MainCard> | |||
| </Grid> | |||
| </ThemeProvider> | |||
| </Grid> | |||
| ); | |||
| }; | |||
| export default FormSigPageSearchPanel; | |||
| @@ -1,7 +1,7 @@ | |||
| // material-ui | |||
| import * as React from 'react'; | |||
| import {apiPath} from "../../../auth/utils"; | |||
| import { POST_SIG_UPLOAD1 } from "../../../utils/ApiPathConst"; | |||
| import { POST_SIG_UPLOAD1, GET_FORM_SIG_PAGE_CONFIG } from "../../../utils/ApiPathConst"; | |||
| import axios from 'axios'; | |||
| import { | |||
| DataGrid, | |||
| @@ -50,6 +50,7 @@ export default function PdfTable({recordList}) { | |||
| const [isDialogOpen, setIsDialogOpen] = React.useState(false); | |||
| // State to hold the ID, templateName, and refType for the current upload operation | |||
| const [currentUploadRow, setCurrentUploadRow] = React.useState(initialUploadState); | |||
| const [currentUploadLabel, setCurrentUploadLabel] = React.useState(null); | |||
| const [isUploading, setIsUploading] = React.useState(false); | |||
| const navigate = useNavigate() | |||
| @@ -72,32 +73,63 @@ export default function PdfTable({recordList}) { | |||
| navigate(`/pdf/maintain/${id}`); | |||
| }; | |||
| /** Safely get YYYY-MM-DD from row.created; returns null if missing or invalid. */ | |||
| const getAsOfDateString = (row) => { | |||
| const raw = row?.created; | |||
| if (raw == null || raw === "") return null; | |||
| const d = new Date(raw); | |||
| if (Number.isNaN(d.getTime())) return null; | |||
| return d.toISOString().slice(0, 10); | |||
| }; | |||
| /** | |||
| * Opens the upload dialog and sets the current row details for Upload 1 | |||
| * Opens the upload dialog and sets the current row details for Upload 1. | |||
| * Fetches form-sig-page config from API for dynamic label (page number). | |||
| */ | |||
| const handleUploadClick = (id, templateName, formCode) => () => { | |||
| const handleUploadClick = (id, templateName, formCode, row) => () => { | |||
| setCurrentUploadRow({ | |||
| id: id, | |||
| templateName: templateName, | |||
| formCode: formCode, | |||
| refType: "upload1" | |||
| }); | |||
| setCurrentUploadLabel(null); | |||
| setIsDialogOpen(true); | |||
| const asOfDate = getAsOfDateString(row); | |||
| const params = new URLSearchParams({ formCode }); | |||
| if (asOfDate) params.set("asOfDate", asOfDate); | |||
| axios.get(`${apiPath}${GET_FORM_SIG_PAGE_CONFIG}?${params}`) | |||
| .then((res) => { | |||
| const configs = res.data?.configs || []; | |||
| const upload1 = configs.find((c) => c.sigType === "upload1"); | |||
| setCurrentUploadLabel(upload1?.label || "Upload Signature"); | |||
| }) | |||
| .catch(() => setCurrentUploadLabel("Upload Signature")); | |||
| }; | |||
| /** | |||
| * Opens the upload dialog and sets the current row details for Upload 2 | |||
| * Opens the upload dialog and sets the current row details for Upload 2. | |||
| * Fetches form-sig-page config from API for dynamic label. | |||
| */ | |||
| const handleUpload2Click = (id, templateName, formCode) => () => { | |||
| // Placeholder for Upload 2 | |||
| console.log(`Uploading for row ID ${id} (Upload 2)`); | |||
| setCurrentUploadRow({ | |||
| const handleUpload2Click = (id, templateName, formCode, row) => () => { | |||
| setCurrentUploadRow({ | |||
| id: id, | |||
| templateName: templateName, | |||
| formCode: formCode, | |||
| refType: "upload2" // A different refType for a different upload logic/API | |||
| refType: "upload2" | |||
| }); | |||
| setIsDialogOpen(true); | |||
| setCurrentUploadLabel(null); | |||
| setIsDialogOpen(true); | |||
| const asOfDate = getAsOfDateString(row); | |||
| const params = new URLSearchParams({ formCode }); | |||
| if (asOfDate) params.set("asOfDate", asOfDate); | |||
| axios.get(`${apiPath}${GET_FORM_SIG_PAGE_CONFIG}?${params}`) | |||
| .then((res) => { | |||
| const configs = res.data?.configs || []; | |||
| const upload2 = configs.find((c) => c.sigType === "upload2"); | |||
| setCurrentUploadLabel(upload2?.label || "Upload 2"); | |||
| }) | |||
| .catch(() => setCurrentUploadLabel("Upload 2")); | |||
| }; | |||
| /** | |||
| @@ -198,60 +230,18 @@ export default function PdfTable({recordList}) { | |||
| const handleCloseDialog = () => { | |||
| setIsDialogOpen(false); | |||
| setCurrentUploadRow(initialUploadState); // Reset the current row | |||
| setCurrentUploadRow(initialUploadState); | |||
| setCurrentUploadLabel(null); | |||
| if (fileInputRef.current) { | |||
| fileInputRef.current.value = ""; // Clear the file input | |||
| } | |||
| }; | |||
| // Function to generate the dynamic dialog title | |||
| // Fallback dialog title when API config is not yet loaded or fails | |||
| const getUploadDialogTitle = (formCode, refType) => { | |||
| console.log("formCode:" + formCode + " refType:" + refType); | |||
| if (refType === 'upload1') { | |||
| switch (formCode) { | |||
| case "IDA": | |||
| return "Upload Page 15"; | |||
| case "FNA": | |||
| return "Upload Page 10"; | |||
| case "HSBCFIN": | |||
| return "Upload Page 11"; | |||
| case "HSBCA31": | |||
| return "Upload Page 28-29"; | |||
| case "MLB03S": | |||
| return "Upload Page 9"; | |||
| case "MLFNA_EN": | |||
| return "Upload Page 4"; | |||
| case "MLFNA_CHI": | |||
| return "Upload Page 4"; | |||
| case "SLGII": | |||
| return "Upload Page 13"; | |||
| case "SLAPP": | |||
| return "Upload Page 17"; | |||
| case "SLFNA_EN": | |||
| return "Upload Page 5"; | |||
| case "SLFNA_CHI": | |||
| return "Upload Page 5"; | |||
| default: | |||
| return "Unknown Form"; | |||
| } | |||
| }else if (refType === 'upload2') { | |||
| switch (formCode) { | |||
| case "MLB03S": | |||
| return "Upload Page 12-13"; | |||
| case "SLGII": | |||
| return "Upload Page 15-16"; | |||
| case "SLAPP": | |||
| return "Upload Page 19-20"; | |||
| default: | |||
| return "Unknown Form"; | |||
| } | |||
| } | |||
| // Handle other refTypes if needed, e.g., 'upload2' | |||
| if (refType === 'upload2') { | |||
| return `Upload Template 2 for ${formCode}`; | |||
| } | |||
| return "Upload File"; // Fallback | |||
| if (refType === "upload1") return "Upload Signature"; | |||
| if (refType === "upload2") return "Upload 2"; | |||
| return "Upload File"; | |||
| }; | |||
| // Function to handle file selection and API submission | |||
| @@ -401,7 +391,7 @@ export default function PdfTable({recordList}) { | |||
| icon={upload1Icon} // Use the dynamic icon | |||
| label={upload1Label} // Use the dynamic label | |||
| className="textPrimary" | |||
| onClick={handleUploadClick(id, templateName, formCode)} | |||
| onClick={handleUploadClick(id, templateName, formCode, row)} | |||
| color="upload" | |||
| /> | |||
| </ThemeProvider> | |||
| @@ -423,7 +413,7 @@ export default function PdfTable({recordList}) { | |||
| icon={upload2Icon} | |||
| label={upload2Label} | |||
| className="textPrimary" | |||
| onClick={handleUpload2Click(id, templateName, formCode)} | |||
| onClick={handleUpload2Click(id, templateName, formCode, row)} | |||
| color="upload" | |||
| /> | |||
| </ThemeProvider> | |||
| @@ -551,8 +541,7 @@ export default function PdfTable({recordList}) { | |||
| TransitionProps={{ onExited: handleCloseDialog }} | |||
| > | |||
| <DialogTitle> | |||
| {/* Dynamic Title based on currentUploadRow state */} | |||
| **{getUploadDialogTitle(currentUploadRow.formCode, currentUploadRow.refType)}** | |||
| {currentUploadLabel != null ? currentUploadLabel : getUploadDialogTitle(currentUploadRow.formCode, currentUploadRow.refType)} | |||
| </DialogTitle> | |||
| <DialogContent dividers> | |||
| <Box sx={{ mt: 2, textAlign: 'center' }}> | |||
| @@ -29,6 +29,7 @@ const EmailConfigPage = Loadable(lazy(() => import('pages/lionerEmailConfig'))); | |||
| const GenerateReminderPage = Loadable(lazy(() => import('pages/lionerManualButtonPage'))); | |||
| const ClientDepartmentPage = Loadable(lazy(() => import('pages/lionerClientDepartmentPage'))); | |||
| const ProfilePage = Loadable(lazy(() => import('pages/profile/profile'))); | |||
| const FormSigPageSearchPanel = Loadable(lazy(() => import('pages/formSigPage'))); | |||
| // ==============================|| AUTH ROUTING ||============================== // | |||
| @@ -260,6 +261,16 @@ const SettingRoutes = () => { | |||
| ) | |||
| ), | |||
| }, | |||
| { | |||
| path: 'formSigPage', | |||
| element: ( | |||
| handleRouteAbility( | |||
| ability.can('MANAGE', 'SYSTEM_CONFIGURATION'), | |||
| <FormSigPageSearchPanel />, | |||
| <Navigate to="/" /> | |||
| ) | |||
| ), | |||
| }, | |||
| { | |||
| path: 'logout', | |||
| element: <LogoutPage /> | |||
| @@ -72,6 +72,9 @@ export const GET_PDF_TEMPLATE_PATH = "/pdf/template" | |||
| export const GET_MERGE_PDF = "/pdf/merge-pdf" | |||
| export const GET_REMOVE_PDF_PASSWORD = "/pdf/remove-pdf-password" | |||
| export const POST_SIG_UPLOAD1 = "/pdf/upload1" | |||
| export const GET_FORM_SIG_PAGE_CONFIG = "/pdf/form-sig-page-config" | |||
| export const GET_FORM_SIG_PAGE_LIST = "/pdf/form-sig-page" | |||
| export const UPDATE_FORM_SIG_PAGE_PATH = "/pdf/form-sig-page/save" | |||
| export const GET_CLIENT_PATH = "/client" | |||
| export const POST_CLIENT_PATH = "/client/save" | |||
| export const GET_EVENT_PATH = "/event" | |||