You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

569 lines
24 KiB

  1. // material-ui
  2. import * as React from 'react';
  3. import {
  4. Stack,
  5. Typography,
  6. Button,
  7. Autocomplete,
  8. TextField,
  9. Grid,
  10. Dialog, DialogTitle, DialogContent, DialogActions, useMediaQuery,
  11. } from '@mui/material';
  12. import { FiDataGrid } from "components/FiDataGrid";
  13. import * as HttpUtils from "utils/HttpUtils"
  14. import * as utils from "auth/utils"
  15. import {
  16. PAYMENT_CHECK,
  17. POST_CHECK_APP_EXPRITY_DATE,
  18. GET_PUBLIC_NOTICE_LIST_ListByStatus_pendingPayment_careOfCombo
  19. } from "utils/ApiPathConst"
  20. import * as DateUtils from "utils/DateUtils"
  21. import * as FormatUtils from "utils/FormatUtils"
  22. import * as StatusUtils from "utils/statusUtils/PublicNoteStatusUtils";
  23. import { useNavigate } from "react-router-dom";
  24. import {
  25. isORGLoggedIn,
  26. isDummyLoggedIn,
  27. } from "utils/Utils";
  28. // import { dateStr } from "utils/DateUtils";
  29. import { ThemeProvider, useTheme } from "@emotion/react";
  30. import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst";
  31. import { FormattedMessage, useIntl } from "react-intl";
  32. // ==============================|| EVENT TABLE ||============================== //
  33. export default function SubmittedTab({ setCount, url }) {
  34. const [rowList, setRowList] = React.useState([]);
  35. const [selectedRowItems, setSelectedRowItems] = React.useState([]);
  36. const [isPopUp, setIsPopUp] = React.useState(false);
  37. const [checkCareOf, setCheckCareOf] = React.useState(false);
  38. const [careOfComboList, setCareOfComboList] = React.useState([]);
  39. const [selectedCareOf, setSelectedCareOf] = React.useState(null);
  40. const [expiryDateErrText, setExpiryDateErrText] = React.useState("");
  41. const [expiryDateErr, setExpiryDateErr] = React.useState(false);
  42. const [paymentHoldedErrText, setPaymentHoldedErrText] = React.useState("");
  43. const [paymentHoldedErr, setPaymentHoldedErr] = React.useState(false);
  44. const [_searchCriteria, set_searchCriteria] = React.useState({});
  45. // const [checkPaymentMethod, setCheckPaymentMethod] = React.useState(false);
  46. const theme = useTheme();
  47. const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
  48. const intl = useIntl();
  49. const { locale } = intl;
  50. //const [amount, setAmount] = React.useState(0);
  51. const navigate = useNavigate()
  52. React.useEffect(() => {
  53. getCareOfList();
  54. }, []);
  55. React.useEffect(() => {
  56. if (selectedCareOf != null) {
  57. set_searchCriteria({ "careOf": selectedCareOf.label });
  58. } else {
  59. set_searchCriteria({});
  60. }
  61. }, [selectedCareOf]);
  62. const getCareOfList = () => {
  63. HttpUtils.get({
  64. url: GET_PUBLIC_NOTICE_LIST_ListByStatus_pendingPayment_careOfCombo,
  65. params: {},
  66. onSuccess: function (responData) {
  67. setCareOfComboList(responData);
  68. }
  69. });
  70. }
  71. const handleDetailClick = (params) => () => {
  72. navigate('/publicNotice/' + params.id);
  73. };
  74. const handlePaymentBtn = () => {
  75. let appIdList = [];
  76. let paymentCheckList = [];
  77. const _datas = rowList;
  78. const datas = _datas?.filter((row) =>
  79. selectedRowItems.includes(row.id)
  80. );
  81. // console.log(datas)
  82. for (var i = 0; i < datas?.length; i++) {
  83. appIdList.push(datas[i].id);
  84. if ( datas[i].paymentMethod != "online"){
  85. paymentCheckList.push(datas[i].paymentMethod)
  86. }
  87. }
  88. // console.log(paymentCheckList)
  89. if(paymentCheckList.length == 0){
  90. if (appIdList.length < 1) {
  91. setExpiryDateErrText(intl.formatMessage({ id: 'MSG.plzSelectApp' }));
  92. setExpiryDateErr(true);
  93. return;
  94. }
  95. HttpUtils.post({
  96. url: POST_CHECK_APP_EXPRITY_DATE,
  97. params: {
  98. ids: appIdList
  99. },
  100. onSuccess: (responData) => {
  101. if (responData.success == true) {
  102. // setIsPopUp(true);
  103. handlePaymentCheck(appIdList)
  104. return;
  105. }
  106. let str = "";
  107. responData.msg.forEach((item) => {
  108. str += "App: " + item.appNo + ", 到期日: " + DateUtils.datetimeStr_Cht(item.expiryDate) + "\n";
  109. });
  110. setExpiryDateErrText(str.split('\n').map(str => <>{str}<br /></>));
  111. setExpiryDateErr(true);
  112. }
  113. });
  114. } else {
  115. setExpiryDateErrText(intl.formatMessage({ id: 'MSG.plzonlinePayment' }));
  116. setExpiryDateErr(true);
  117. return;
  118. }
  119. };
  120. const handlePaymentCheck = (appIdList) => {
  121. HttpUtils.post({
  122. url: PAYMENT_CHECK,
  123. params: {
  124. appIds: appIdList
  125. },
  126. onSuccess: (responseData) => {
  127. const latestData = {};
  128. responseData.forEach(item => {
  129. // console.log(item)
  130. const { appId, timeDiff } = item;
  131. if (latestData[appId] === undefined || timeDiff < latestData[appId].timeDiff) {
  132. latestData[appId] = item;
  133. }
  134. });
  135. const latestDataObjects = Object.values(latestData);
  136. const filteredData = latestDataObjects.filter(item => item.timeDiff > 30 || item.status == "CANC" || item.status == "REJT");
  137. const filteredAppIds = filteredData.map(item => item.appId);
  138. const appIdsNotInData = appIdList.filter(appId => !latestDataObjects.some(item => item.appId === appId));
  139. const combinedAppIdsArray = [...appIdsNotInData, ...filteredAppIds];
  140. const readyToPayment = appIdList.every(appId => combinedAppIdsArray.includes(appId));
  141. if (readyToPayment) {
  142. setIsPopUp(true);
  143. return;
  144. } else {
  145. const appIdsInData = appIdList.filter(appId => !combinedAppIdsArray.some(item => item === appId));
  146. const HoldingApplication = latestDataObjects.filter(item => appIdsInData.includes(item.appId));
  147. const resultString = HoldingApplication.map(item => item.appNo).join(' , ');
  148. setPaymentHoldedErrText(resultString);
  149. // setPaymentHoldedErrText(intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: record.appNo }));
  150. setPaymentHoldedErr(true);
  151. }
  152. }
  153. });
  154. };
  155. const columns = [
  156. {
  157. id: 'appNo',
  158. field: 'appNo',
  159. headerName: intl.formatMessage({ id: 'applicationId' }),
  160. width: isMdOrLg ? 'auto' : 160,
  161. flex: isMdOrLg ? 1 : undefined,
  162. },
  163. {
  164. id: 'created',
  165. field: 'created',
  166. headerName: intl.formatMessage({ id: 'submitDate' }),
  167. width: isMdOrLg ? 'auto' : 160,
  168. flex: isMdOrLg ? 1 : undefined,
  169. valueGetter: (params) => {
  170. return DateUtils.datetimeStr(params.value);
  171. }
  172. },
  173. {
  174. id: 'remarks',
  175. field: 'remarks',
  176. headerName: isORGLoggedIn() ? intl.formatMessage({ id: 'gazetteCount2_1' }) : intl.formatMessage({ id: 'myRemarks' }),
  177. width: isMdOrLg ? 'auto' : 400,
  178. flex: isMdOrLg ? 3 : undefined,
  179. renderCell: (params) => (
  180. isORGLoggedIn() ?
  181. isDummyLoggedIn()?
  182. <div>
  183. <FormattedMessage id="gazetteCount" />: {params.row.issueVolume + "/" + params.row.issueYear
  184. + " No. " + params.row.issueNo}<br />
  185. GLD: {params.row.custName} <br />
  186. <FormattedMessage id="careOf" />: {params.row.careOf} <br />
  187. <FormattedMessage id="myRemarks" />: {params.row.remarks}
  188. </div>:
  189. <div>
  190. <FormattedMessage id="gazetteCount" />: {params.row.issueVolume + "/" + params.row.issueYear
  191. + " No. " + params.row.issueNo}<br />
  192. <FormattedMessage id="careOf" />: {params.row.careOf} <br />
  193. <FormattedMessage id="myRemarks" />: {params.row.remarks}
  194. </div>
  195. :
  196. <div>
  197. <FormattedMessage id="gazetteCount" />: {params.row.issueVolume + "/" + params.row.issueYear
  198. + " No. " + params.row.issueNo}<br />
  199. <FormattedMessage id="myRemarks" />:{params.row.remarks}
  200. </div>
  201. )
  202. },
  203. {
  204. id: 'fee',
  205. field: 'fee',
  206. headerName: intl.formatMessage({ id: 'price' }),
  207. width: isMdOrLg ? 'auto' : 160,
  208. flex: isMdOrLg ? 1 : undefined,
  209. renderCell: (params) => {
  210. return FormatUtils.currencyFormat(params.row.fee)
  211. },
  212. },
  213. {
  214. id: 'paymentMethodAndDeadLine',
  215. field: 'paymentMethodAndDeadLine',
  216. headerName: intl.formatMessage({ id: 'paymentMethodAndDeadLine' }),
  217. width: isMdOrLg ? 'auto' : 250,
  218. flex: isMdOrLg ? 2 : undefined,
  219. renderCell: (params) => (
  220. <div>
  221. <FormattedMessage id={utils.getPaymentMethod(params.row.paymentMethod)} /><br />
  222. <div>
  223. {/* {dateStr(params.row.closingDate)} */}
  224. {
  225. params.row.paymentMethod=="online"?
  226. locale === 'en' ?
  227. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 14, 30)?.replace("am", "a.m.")?.replace("pm", "p.m.")}`
  228. :
  229. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 14, 30)?.replace("am", "上午")?.replace("pm", "下午").replace("00分", "")}`
  230. :params.row.paymentMethod=="demandNote" ?
  231. locale === 'en' ?
  232. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 12, 0)?.replace("am", "a.m.")?.replace("pm", "p.m.")}`
  233. :
  234. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 12, 0)?.replace("am", "上午")?.replace("pm", "下午").replace("00分", "")}`
  235. :
  236. locale === 'en' ?
  237. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 12, 30)?.replace("am", "a.m.")?.replace("pm", "p.m.")}`
  238. :
  239. `${DateUtils.dateFormatWithFix(params.row.expiryDate, intl.formatMessage({ id: "datetimeFormate" }), 12, 30)?.replace("am", "上午")?.replace("pm", "下午").replace("00分", "")}`
  240. }
  241. {/* {
  242. locale === 'en' ?
  243. `${DateUtils.dateFormatWithFix(params.row.closingDate, intl.formatMessage({ id: "datetimeFormate" }), 14, 30)?.replace("am", "a.m.")?.replace("pm", "p.m.")}`
  244. :
  245. `${DateUtils.dateFormatWithFix(params.row.closingDate, intl.formatMessage({ id: "datetimeFormate" }), 14, 30)?.replace("am", "上午")?.replace("pm", "下午").replace("00分", "")}`
  246. }
  247. {params.row.paymentMethod=="online" ? " 2:30pm"
  248. :params.row.paymentMethod=="demandNote" ? " 12:00pm"
  249. : " 12:30pm"} */}
  250. </div>
  251. </div>
  252. )
  253. },
  254. // {
  255. // id: 'closingDateOff',
  256. // field: 'closingDateOff',
  257. // headerName: intl.formatMessage({ id: 'paymentMethod' }),
  258. // width: isMdOrLg ? 'auto' : 160,
  259. // flex: isMdOrLg ? 1 : undefined,
  260. // renderCell: (params) => {
  261. // // console.log(params)
  262. // let closingDateOff = params.row.closingDateOff;
  263. // return <div style={{ margin: 4 }}>{dateStr(closingDateOff)}</div>
  264. // },
  265. // },
  266. {
  267. id: 'status',
  268. field: 'status',
  269. headerName: intl.formatMessage({ id: 'status' }),
  270. width: isMdOrLg ? 'auto' : 160,
  271. flex: isMdOrLg ? 1 : undefined,
  272. renderCell: (params) => {
  273. return [StatusUtils.getStatusIntl(params, intl)]
  274. },
  275. },
  276. {
  277. field: 'actions',
  278. type: 'actions',
  279. headerName: '',
  280. width: 150,
  281. cellClassName: 'actions',
  282. renderCell: (params) => {
  283. return <Button aria-label={intl.formatMessage({ id: 'viewDetail' })} onClick={handleDetailClick(params)}>
  284. <FormattedMessage id="viewDetail" />
  285. </Button>;
  286. },
  287. }
  288. ];
  289. const getWindowContent = () => {
  290. var content = [];
  291. let totalAmount = 0;
  292. const _datas = rowList;
  293. const datas = _datas?.filter((row) =>
  294. selectedRowItems.includes(row.id)
  295. );
  296. for (var i = 0; i < datas?.length; i++) {
  297. content.push(<>
  298. <Stack direction="row" justifyContent="space-between">
  299. <Typography variant="h5">
  300. <FormattedMessage id="applicationId" />: {datas[i].appNo}
  301. </Typography>
  302. ({DateUtils.datetimeStr(datas[i].created)})
  303. </Stack>
  304. <FormattedMessage id="extraMark" />: {datas[i].remarks}
  305. <br /><br />
  306. </>);
  307. totalAmount += datas[i].fee;
  308. }
  309. content.push(<Typography variant="h5">
  310. <FormattedMessage id="totalAmount" /> ($): {FormatUtils.currencyFormat(totalAmount)}
  311. <br /><br />
  312. </Typography>);
  313. return content;
  314. }
  315. function handleRowDoubleClick(params) {
  316. navigate('/publicNotice/' + params.id);
  317. }
  318. function doPayment() {
  319. setIsPopUp(false);
  320. let totalAmount = 0;
  321. let appIdList = [];
  322. const _datas = rowList;
  323. const datas = _datas?.filter((row) =>
  324. selectedRowItems.includes(row.id)
  325. );
  326. // console.log(datas)
  327. for (var i = 0; i < datas?.length; i++) {
  328. totalAmount += datas[i].fee;
  329. appIdList.push(datas[i].id);
  330. }
  331. const firstCareOf = datas[0].careOf;
  332. const areAllCareOfEqual = datas.every(obj => obj.careOf === firstCareOf);
  333. if (appIdList.length > 0 && areAllCareOfEqual) {
  334. navigate('/paymentPage', { state: { amount: totalAmount, appIdList: appIdList } });
  335. } else {
  336. setCheckCareOf(true);
  337. // console.log("The selected applications should be from the same Care of.")
  338. }
  339. }
  340. function afterWarningPayment() {
  341. let totalAmount = 0;
  342. let appIdList = [];
  343. const _datas = rowList;
  344. const datas = _datas?.filter((row) =>
  345. selectedRowItems.includes(row.id)
  346. );
  347. // console.log(datas)
  348. for (var i = 0; i < datas?.length; i++) {
  349. totalAmount += datas[i].fee;
  350. appIdList.push(datas[i].id);
  351. }
  352. navigate('/paymentPage', { state: { amount: totalAmount, appIdList: appIdList } });
  353. }
  354. return (
  355. <>
  356. <div style={{ minHeight: 400, width: '100%', padding: 4 }}>
  357. {isORGLoggedIn() ?
  358. <Grid container direction="row" justifyContent="flex-start" alignItems="center" >
  359. <Grid item xs={3} md={1}>
  360. <Typography variant="h5"><FormattedMessage id="careOf" />:</Typography>
  361. </Grid>
  362. <Grid item xs={8} md={2}>
  363. <Autocomplete
  364. disablePortal
  365. id="careOfCombo"
  366. value={selectedCareOf === null ? null : selectedCareOf}
  367. options={careOfComboList}
  368. onChange={(event, newValue) => {
  369. // console.log(newValue)
  370. setSelectedCareOf(newValue);
  371. }}
  372. renderInput={(params) => <TextField {...params} />}
  373. />
  374. </Grid>
  375. </Grid> : null
  376. }
  377. <FiDataGrid
  378. checkboxSelection
  379. disableRowSelectionOnClick
  380. columns={columns}
  381. customPageSize={10}
  382. onRowSelectionModelChange={(newSelection) => {
  383. setSelectedRowItems(newSelection);
  384. }}
  385. onRowDoubleClick={handleRowDoubleClick}
  386. getRowHeight={() => 'auto'}
  387. doLoad={React.useMemo(() => ({
  388. url: url,
  389. params: _searchCriteria,
  390. callback: function (responseData) {
  391. setCount(responseData?.count ?? 0);
  392. setRowList(responseData?.records);
  393. }
  394. }),[url, _searchCriteria])}
  395. />
  396. <ThemeProvider theme={PNSPS_BUTTON_THEME}>
  397. <Button
  398. color="create"
  399. variant="contained"
  400. aria-label={intl.formatMessage({ id: 'payOnlineBtn' })}
  401. onClick={() => { handlePaymentBtn() }}
  402. sx={{ mt: 2, ml: 1 }}
  403. >
  404. <FormattedMessage id="payOnlineBtn" />
  405. </Button>
  406. </ThemeProvider>
  407. </div>
  408. <div>
  409. <Dialog
  410. open={isPopUp}
  411. onClose={() => setIsPopUp(false)}
  412. PaperProps={{
  413. sx: {
  414. minWidth: '40vw',
  415. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '30vw' },
  416. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  417. }
  418. }}
  419. >
  420. <DialogTitle>
  421. <Typography variant="h3" >
  422. <FormattedMessage id="payConfirm" />
  423. </Typography>
  424. </DialogTitle>
  425. <DialogContent style={{ display: 'flex', }}>
  426. <Stack direction="column" justifyContent="space-between">
  427. {getWindowContent()}
  428. </Stack>
  429. </DialogContent>
  430. <DialogActions>
  431. <Button onClick={() => setIsPopUp(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  432. <Typography variant="h5">
  433. <FormattedMessage id="close" />
  434. </Typography></Button>
  435. <Button onClick={() => doPayment()} aria-label={intl.formatMessage({ id: 'confirm' })}>
  436. <Typography variant="h5">
  437. <FormattedMessage id="confirm" />
  438. </Typography></Button>
  439. </DialogActions>
  440. </Dialog>
  441. </div>
  442. <div>
  443. <Dialog
  444. open={checkCareOf}
  445. onClose={() => setCheckCareOf(false)}
  446. PaperProps={{
  447. sx: {
  448. minWidth: '40vw',
  449. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  450. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  451. }
  452. }}
  453. >
  454. <DialogTitle></DialogTitle>
  455. <Typography variant="h2" style={{ padding: '16px' }}>
  456. <FormattedMessage id="warning" />
  457. </Typography>
  458. <DialogContent style={{ display: 'flex', }}>
  459. <Stack direction="column" justifyContent="space-between">
  460. <Typography variant="h5" color="error">
  461. <FormattedMessage id="careOfWarning" />
  462. </Typography>
  463. </Stack>
  464. </DialogContent>
  465. <DialogActions>
  466. <Button onClick={() => setCheckCareOf(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  467. <Typography variant="h5">
  468. <FormattedMessage id="close" />
  469. </Typography></Button>
  470. <Button onClick={() => afterWarningPayment()} aria-label={intl.formatMessage({ id: 'confirm' })}>
  471. <Typography variant="h5">
  472. <FormattedMessage id="confirm" />
  473. </Typography></Button>
  474. </DialogActions>
  475. </Dialog>
  476. </div>
  477. <div>
  478. <Dialog
  479. open={expiryDateErr}
  480. onClose={() => setExpiryDateErr(false)}
  481. PaperProps={{
  482. sx: {
  483. minWidth: '40vw',
  484. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  485. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  486. }
  487. }}
  488. >
  489. <DialogTitle></DialogTitle>
  490. <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography>
  491. <DialogContent style={{ display: 'flex', }}>
  492. <Stack direction="column" justifyContent="space-between">
  493. {
  494. expiryDateErrText
  495. }
  496. </Stack>
  497. </DialogContent>
  498. <DialogActions>
  499. <Button onClick={() => setExpiryDateErr(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  500. <Typography variant="h5">
  501. <FormattedMessage id="close" />
  502. </Typography></Button>
  503. </DialogActions>
  504. </Dialog>
  505. </div>
  506. <div>
  507. <Dialog
  508. open={paymentHoldedErr}
  509. onClose={() => setPaymentHoldedErr(false)}
  510. PaperProps={{
  511. sx: {
  512. minWidth: '40vw',
  513. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  514. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  515. }
  516. }}
  517. >
  518. <DialogTitle></DialogTitle>
  519. <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography>
  520. <DialogContent style={{ display: 'flex', }}>
  521. <Stack direction="column" justifyContent="space-between">
  522. <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} />
  523. </Stack>
  524. </DialogContent>
  525. <DialogActions>
  526. <Button onClick={() => setPaymentHoldedErr(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  527. <Typography variant="h5">
  528. <FormattedMessage id="close" />
  529. </Typography></Button>
  530. </DialogActions>
  531. </Dialog>
  532. </div>
  533. </>
  534. );
  535. }