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.
 
 

485 Zeilen
19 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 UrlUtils from "utils/ApiPathConst"
  15. import * as DateUtils from "utils/DateUtils"
  16. import * as FormatUtils from "utils/FormatUtils"
  17. import * as StatusUtils from "utils/statusUtils/PublicNoteStatusUtils";
  18. import { useNavigate } from "react-router-dom";
  19. import {
  20. isORGLoggedIn,
  21. } from "utils/Utils";
  22. import { ThemeProvider, useTheme } from "@emotion/react";
  23. import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst";
  24. import { FormattedMessage, useIntl } from "react-intl";
  25. // ==============================|| EVENT TABLE ||============================== //
  26. export default function SubmittedTab({ rows }) {
  27. const [rowList, setRowList] = React.useState([]);
  28. const [selectedRowItems, setSelectedRowItems] = React.useState([]);
  29. const [isPopUp, setIsPopUp] = React.useState(false);
  30. const [checkCareOf, setCheckCareOf] = React.useState(false);
  31. const [getCareOfList, setCareOfList] = React.useState([]);
  32. const [careOfComboList, setCareOfComboList] = React.useState([{}]);
  33. const [selectedCareOf, setSelectedCareOf] = React.useState(null);
  34. const [expiryDateErrText, setExpiryDateErrText] = React.useState("");
  35. const [expiryDateErr, setExpiryDateErr] = React.useState(false);
  36. const [paymentHoldedErrText, setPaymentHoldedErrText] = React.useState("");
  37. const [paymentHoldedErr, setPaymentHoldedErr] = React.useState(false);
  38. const theme = useTheme();
  39. const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
  40. const intl = useIntl();
  41. //const [amount, setAmount] = React.useState(0);
  42. const navigate = useNavigate()
  43. React.useEffect(() => {
  44. // const careOfList = rows.map(obj => obj.careOf);
  45. // console.log(rows)
  46. // console.log(careOfList)
  47. setRowList(rows)
  48. const formattedData = Array.from(new Set(rows.filter(obj => obj.careOf !== null).map(obj => obj.careOf))).map((careOf, index) => ({
  49. key: index,
  50. id: rows.find(obj => obj.careOf === careOf).id,
  51. label: careOf
  52. }));
  53. // console.log(formattedData)
  54. setCareOfList(formattedData)
  55. }, []);
  56. React.useEffect(() => {
  57. // console.log(getCareOfList)
  58. setCareOfComboList(getCareOfList)
  59. }, [getCareOfList]);
  60. React.useEffect(() => {
  61. if (selectedCareOf != null) {
  62. const afteSelectedList = [];
  63. console.log(rows)
  64. console.log(selectedCareOf)
  65. rows.forEach((element) => {
  66. if (element.careOf === selectedCareOf.label) {
  67. afteSelectedList.push(element)
  68. }
  69. });
  70. // afteSelectedList.push(rows.find(obj => obj.careOf ===(selectedCareOf.label)));
  71. console.log(afteSelectedList)
  72. setRowList(afteSelectedList)
  73. } else {
  74. setRowList(rows)
  75. }
  76. }, [selectedCareOf]);
  77. const handleDetailClick = (params) => () => {
  78. navigate('/publicNotice/' + params.id);
  79. };
  80. const handlePaymentBtn = () => {
  81. let appIdList = [];
  82. const datas = rows?.filter((row) =>
  83. selectedRowItems.includes(row.id)
  84. );
  85. // console.log(datas)
  86. for (var i = 0; i < datas?.length; i++) {
  87. appIdList.push(datas[i].id);
  88. }
  89. if(appIdList.length<1){
  90. setExpiryDateErrText(intl.formatMessage({ id: 'MSG.plzSelectApp' }));
  91. setExpiryDateErr(true);
  92. return;
  93. }
  94. HttpUtils.post({
  95. url: UrlUtils.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. };
  114. const handlePaymentCheck = (appIdList) => {
  115. HttpUtils.post({
  116. url: UrlUtils.PAYMENT_CHECK,
  117. params: {
  118. appIds: appIdList
  119. },
  120. onSuccess: (responseData) => {
  121. const latestData = {};
  122. responseData.forEach(item => {
  123. const { appId, timeDiff } = item;
  124. if (latestData[appId] === undefined || timeDiff < latestData[appId].timeDiff) {
  125. latestData[appId] = item;
  126. }
  127. });
  128. const latestDataObjects = Object.values(latestData);
  129. const filteredData = latestDataObjects.filter(item => item.timeDiff > 20 || item.status !== "APPR");
  130. const filteredAppIds = filteredData.map(item => item.appId);
  131. const appIdsNotInData = appIdList.filter(appId => !latestDataObjects.some(item => item.appId === appId));
  132. const combinedAppIdsArray = [...appIdsNotInData, ...filteredAppIds];
  133. const readyToPayment = appIdList.every(appId => combinedAppIdsArray.includes(appId));
  134. if (readyToPayment){
  135. setIsPopUp(true);
  136. return;
  137. }else{
  138. const appIdsInData = appIdList.filter(appId => !combinedAppIdsArray.some(item => item === appId));
  139. const HoldingApplication = latestDataObjects.filter(item => appIdsInData.includes(item.appId));
  140. const resultString = HoldingApplication.map(item => item.appNo).join(' , ');
  141. setPaymentHoldedErrText(resultString);
  142. // setPaymentHoldedErrText(intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: record.appNo }));
  143. setPaymentHoldedErr(true);
  144. }
  145. }
  146. });
  147. };
  148. const columns = [
  149. {
  150. id: 'appNo',
  151. field: 'appNo',
  152. headerName: intl.formatMessage({ id: 'applicationId' }),
  153. width: isMdOrLg ? 'auto' : 160,
  154. flex: isMdOrLg ? 1 : undefined,
  155. },
  156. {
  157. id: 'created',
  158. field: 'created',
  159. headerName: intl.formatMessage({ id: 'submitDate' }),
  160. width: isMdOrLg ? 'auto' : 160,
  161. flex: isMdOrLg ? 1 : undefined,
  162. valueGetter: (params) => {
  163. return DateUtils.datetimeStr(params.value);
  164. }
  165. },
  166. {
  167. id: 'remarks',
  168. field: 'remarks',
  169. headerName: isORGLoggedIn() ? intl.formatMessage({ id: 'gazetteCount2_1' }) : intl.formatMessage({ id: 'myRemarks' }),
  170. width: isMdOrLg ? 'auto' : 400,
  171. flex: isMdOrLg ? 3 : undefined,
  172. renderCell: (params) => (
  173. isORGLoggedIn() ?
  174. <div>
  175. <FormattedMessage id="careOf" />: {params.row.careOf}<br />
  176. <FormattedMessage id="myRemarks" />: {params.row.remarks}
  177. </div> :
  178. <div>
  179. {params.row.remarks}
  180. </div>
  181. )
  182. },
  183. {
  184. id: 'fee',
  185. field: 'fee',
  186. headerName: intl.formatMessage({ id: 'price' }),
  187. width: isMdOrLg ? 'auto' : 160,
  188. flex: isMdOrLg ? 1 : undefined,
  189. renderCell: (params) => {
  190. return FormatUtils.currencyFormat(params.row.fee)
  191. },
  192. },
  193. {
  194. id: 'status',
  195. field: 'status',
  196. headerName: intl.formatMessage({ id: 'status' }),
  197. width: isMdOrLg ? 'auto' : 160,
  198. flex: isMdOrLg ? 1 : undefined,
  199. renderCell: (params) => {
  200. return [StatusUtils.getStatusIntl(params, intl)]
  201. },
  202. },
  203. {
  204. field: 'actions',
  205. type: 'actions',
  206. headerName: '',
  207. width: 150,
  208. cellClassName: 'actions',
  209. renderCell: (params) => {
  210. return <Button aria-label={intl.formatMessage({ id: 'viewDetail' })} onClick={handleDetailClick(params)}>
  211. <FormattedMessage id="viewDetail" />
  212. </Button>;
  213. },
  214. }
  215. ];
  216. const getWindowContent = () => {
  217. var content = [];
  218. let totalAmount = 0;
  219. const datas = rows?.filter((row) =>
  220. selectedRowItems.includes(row.id)
  221. );
  222. for (var i = 0; i < datas?.length; i++) {
  223. content.push(<>
  224. <Stack direction="row" justifyContent="space-between">
  225. <Typography variant="h5">
  226. <FormattedMessage id="applicationId" />: {datas[i].appNo}
  227. </Typography>
  228. ({DateUtils.datetimeStr(datas[i].created)})
  229. </Stack>
  230. <FormattedMessage id="extraMark" />: {datas[i].remarks}
  231. <br /><br />
  232. </>);
  233. totalAmount += datas[i].fee;
  234. }
  235. content.push(<Typography variant="h5">
  236. <FormattedMessage id="totalAmount" /> (HK$): {FormatUtils.currencyFormat(totalAmount)}
  237. <br /><br />
  238. </Typography>);
  239. return content;
  240. }
  241. function handleRowDoubleClick(params) {
  242. navigate('/publicNotice/' + params.id);
  243. }
  244. function doPayment() {
  245. setIsPopUp(false);
  246. let totalAmount = 0;
  247. let appIdList = [];
  248. const datas = rows?.filter((row) =>
  249. selectedRowItems.includes(row.id)
  250. );
  251. // console.log(datas)
  252. for (var i = 0; i < datas?.length; i++) {
  253. totalAmount += datas[i].fee;
  254. appIdList.push(datas[i].id);
  255. }
  256. const firstCareOf = datas[0].careOf;
  257. const areAllCareOfEqual = datas.every(obj => obj.careOf === firstCareOf);
  258. if (appIdList.length > 0 && areAllCareOfEqual) {
  259. navigate('/paymentPage', { state: { amount: totalAmount, appIdList: appIdList } });
  260. } else {
  261. setCheckCareOf(true);
  262. // console.log("The selected applications should be from the same Care of.")
  263. }
  264. }
  265. function afterWarningPayment() {
  266. let totalAmount = 0;
  267. let appIdList = [];
  268. const datas = rows?.filter((row) =>
  269. selectedRowItems.includes(row.id)
  270. );
  271. // console.log(datas)
  272. for (var i = 0; i < datas?.length; i++) {
  273. totalAmount += datas[i].fee;
  274. appIdList.push(datas[i].id);
  275. }
  276. navigate('/paymentPage', { state: { amount: totalAmount, appIdList: appIdList } });
  277. }
  278. return (
  279. <>
  280. <div style={{ minHeight: 400, width: '100%', padding: 4 }}>
  281. {isORGLoggedIn() ?
  282. <Grid container direction="row" justifyContent="flex-start" alignItems="center" >
  283. <Grid item xs={3} md={1}>
  284. <Typography variant="h5"><FormattedMessage id="careOf" />:</Typography>
  285. </Grid>
  286. <Grid item xs={8} md={2}>
  287. <Autocomplete
  288. disablePortal
  289. id="careOfCombo"
  290. value={selectedCareOf === null ? null : selectedCareOf}
  291. options={careOfComboList}
  292. onChange={(event, newValue) => {
  293. // console.log(newValue)
  294. setSelectedCareOf(newValue);
  295. }}
  296. renderInput={(params) => <TextField {...params} />}
  297. />
  298. </Grid>
  299. </Grid> : null
  300. }
  301. <FiDataGrid
  302. checkboxSelection
  303. disableRowSelectionOnClick
  304. rows={rowList}
  305. columns={columns}
  306. customPageSize={20}
  307. onRowSelectionModelChange={(newSelection) => {
  308. setSelectedRowItems(newSelection);
  309. }}
  310. onRowDoubleClick={handleRowDoubleClick}
  311. getRowHeight={() => 'auto'}
  312. />
  313. <ThemeProvider theme={PNSPS_BUTTON_THEME}>
  314. <Button
  315. color="create"
  316. variant="contained"
  317. aria-label={intl.formatMessage({ id: 'pay' })}
  318. onClick={() => { handlePaymentBtn() }}
  319. sx={{ mt: 2, ml: 1 }}
  320. >
  321. <FormattedMessage id="pay" />
  322. </Button>
  323. </ThemeProvider>
  324. </div>
  325. <div>
  326. <Dialog
  327. open={isPopUp}
  328. onClose={() => setIsPopUp(false)}
  329. PaperProps={{
  330. sx: {
  331. minWidth: '40vw',
  332. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '30vw' },
  333. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  334. }
  335. }}
  336. >
  337. <DialogTitle>
  338. <Typography variant="h3" >
  339. <FormattedMessage id="payConfirm" />
  340. </Typography>
  341. </DialogTitle>
  342. <DialogContent style={{ display: 'flex', }}>
  343. <Stack direction="column" justifyContent="space-between">
  344. {getWindowContent()}
  345. </Stack>
  346. </DialogContent>
  347. <DialogActions>
  348. <Button onClick={() => setIsPopUp(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  349. <Typography variant="h5">
  350. <FormattedMessage id="close" />
  351. </Typography></Button>
  352. <Button onClick={() => doPayment()} aria-label={intl.formatMessage({ id: 'confirm' })}>
  353. <Typography variant="h5">
  354. <FormattedMessage id="confirm" />
  355. </Typography></Button>
  356. </DialogActions>
  357. </Dialog>
  358. </div>
  359. <div>
  360. <Dialog
  361. open={checkCareOf}
  362. onClose={() => setCheckCareOf(false)}
  363. PaperProps={{
  364. sx: {
  365. minWidth: '40vw',
  366. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  367. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  368. }
  369. }}
  370. >
  371. <DialogTitle></DialogTitle>
  372. <Typography variant="h2" style={{ padding: '16px' }}>
  373. <FormattedMessage id="warning" />
  374. </Typography>
  375. <DialogContent style={{ display: 'flex', }}>
  376. <Stack direction="column" justifyContent="space-between">
  377. <Typography variant="h5" color="error">
  378. <FormattedMessage id="careOfWarning" />
  379. </Typography>
  380. </Stack>
  381. </DialogContent>
  382. <DialogActions>
  383. <Button onClick={() => setCheckCareOf(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  384. <Typography variant="h5">
  385. <FormattedMessage id="close" />
  386. </Typography></Button>
  387. <Button onClick={() => afterWarningPayment()} aria-label={intl.formatMessage({ id: 'confirm' })}>
  388. <Typography variant="h5">
  389. <FormattedMessage id="confirm" />
  390. </Typography></Button>
  391. </DialogActions>
  392. </Dialog>
  393. </div>
  394. <div>
  395. <Dialog
  396. open={expiryDateErr}
  397. onClose={() => setExpiryDateErr(false)}
  398. PaperProps={{
  399. sx: {
  400. minWidth: '40vw',
  401. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  402. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  403. }
  404. }}
  405. >
  406. <DialogTitle></DialogTitle>
  407. <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography>
  408. <DialogContent style={{ display: 'flex', }}>
  409. <Stack direction="column" justifyContent="space-between">
  410. {
  411. expiryDateErrText
  412. }
  413. </Stack>
  414. </DialogContent>
  415. <DialogActions>
  416. <Button onClick={() => setExpiryDateErr(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  417. <Typography variant="h5">
  418. <FormattedMessage id="close" />
  419. </Typography></Button>
  420. </DialogActions>
  421. </Dialog>
  422. </div>
  423. <div>
  424. <Dialog
  425. open={paymentHoldedErr}
  426. onClose={() => setPaymentHoldedErr(false)}
  427. PaperProps={{
  428. sx: {
  429. minWidth: '40vw',
  430. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  431. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  432. }
  433. }}
  434. >
  435. <DialogTitle></DialogTitle>
  436. <Typography variant="h4" style={{ paddingLeft: '24px' }}><FormattedMessage id="MSG.actionFail" /></Typography>
  437. <DialogContent style={{ display: 'flex', }}>
  438. <Stack direction="column" justifyContent="space-between">
  439. <div dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: 'MSG.paymentHolded' }, { appNo: paymentHoldedErrText }) }} />
  440. </Stack>
  441. </DialogContent>
  442. <DialogActions>
  443. <Button onClick={() => setPaymentHoldedErr(false)} aria-label={intl.formatMessage({ id: 'close' })}>
  444. <Typography variant="h5">
  445. <FormattedMessage id="close" />
  446. </Typography></Button>
  447. </DialogActions>
  448. </Dialog>
  449. </div>
  450. </>
  451. );
  452. }