Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

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