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.
 
 

618 lines
34 KiB

  1. // material-ui
  2. import {
  3. Grid,
  4. Typography,
  5. Button,
  6. RadioGroup,
  7. Checkbox,
  8. Dialog, DialogTitle, DialogContent, DialogActions,
  9. Stack, Box
  10. } from '@mui/material';
  11. import { useFormik } from 'formik';
  12. import * as yup from 'yup';
  13. import * as HttpUtils from "utils/HttpUtils";
  14. import * as UrlUtils from "utils/ApiPathConst";
  15. import * as FieldUtils from "utils/FieldUtils";
  16. import * as DateUtils from "utils/DateUtils";
  17. import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
  18. import ForwardIcon from '@mui/icons-material/Forward';
  19. import {
  20. isORGLoggedIn,
  21. isDummyLoggedIn
  22. } from "utils/Utils";
  23. import { useNavigate } from "react-router-dom";
  24. import { notifyActionSuccess } from 'utils/CommonFunction';
  25. import { PNSPS_LONG_BUTTON_THEME } from "../../../themes/buttonConst";
  26. import { ThemeProvider } from "@emotion/react";
  27. import { FormattedMessage, useIntl } from "react-intl";
  28. import Loadable from 'components/Loadable';
  29. import { useState, useEffect, lazy } from 'react';
  30. const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent')));
  31. // ==============================|| DASHBOARD - DEFAULT ||============================== //
  32. const PublicNoticeApplyForm = ({ loadedData, _selections, gazetteIssueList }) => {
  33. const [isWarningPopUp, setIsWarningPopUp] = useState(false);
  34. const [warningTitle, setWarningTitle] = useState("");
  35. const [warningText, setWarningText] = useState("");
  36. const [attachment, setAttachment] = useState({});
  37. const [selections, setsSelections] = useState(<></>);
  38. const intl = useIntl();
  39. const { locale } = intl;
  40. const dft = locale === 'en' ? "DD MMM YYYY" : "YYYY年MM月DD日";
  41. const [val, setVal] = useState({});
  42. const [reloadPage, setReloadPage] = useState(false);
  43. const [isSubmitting, setSubmitting] = useState(false);
  44. const [tickAccept, setTickAccept] = useState(false);
  45. const [issueId, setIssueId] = useState(loadedData.issueId);
  46. const [closeDate, setCloseDate] = useState(null);
  47. const [closingDateOff, setClosingDateOff] = useState(null);
  48. const navigate = useNavigate();
  49. const BackgroundHead = {
  50. backgroundImage: `url(${titleBackgroundImg})`,
  51. width: 'auto',
  52. height: 'auto',
  53. backgroundSize: 'contain',
  54. backgroundRepeat: 'no-repeat',
  55. backgroundColor: '#0C489E',
  56. backgroundPosition: 'right'
  57. }
  58. const tabelStyle = {
  59. border: "2px solid gray",
  60. borderCollapse: "collapse",
  61. padding: "right"
  62. }
  63. function getMaxErrStr(num, fieldname) {
  64. return intl.formatMessage({ id: 'noMoreThenNWords' }, { num: num, fieldname: fieldname ? intl.formatMessage({ id: fieldname }) + ": " : "" });
  65. }
  66. useEffect(() => {
  67. setsSelections(_selections)
  68. }, [_selections]);
  69. useEffect(() => {
  70. for (var i = 0; i < gazetteIssueList?.length; i++) {
  71. let data = gazetteIssueList[i];
  72. if (data.id == issueId) {
  73. setCloseDate(data.closingDate)
  74. setClosingDateOff(data.closingDateOff)
  75. break;
  76. }
  77. }
  78. }, [issueId]);
  79. // function displayErrorMsg(errorMsg) {
  80. // return <Typography variant="errorMessage1">{errorMsg}</Typography>
  81. // }
  82. const formik = useFormik({
  83. enableReinitialize: true,
  84. initialValues: loadedData,
  85. validationSchema: yup.object().shape({
  86. contactPerson: yup.string().max(40, intl.formatMessage({ id: 'noMoreThen40Words' })).required(intl.formatMessage({ id: 'requireContactPerson' })).nullable(),
  87. tel_countryCode: yup.string().min(3, intl.formatMessage({ id: 'require3Number' })).required(intl.formatMessage({ id: 'requireDialingCode' })),
  88. fax_countryCode: yup.string().min(3, intl.formatMessage({ id: 'require3Number' })),
  89. phoneNumber: yup.string().min(8, intl.formatMessage({ id: 'require8Number' })).required(intl.formatMessage({ id: 'requireContactNumber' })),
  90. faxNumber: yup.string().min(8, intl.formatMessage({ id: 'require8Number' })),
  91. remarks: yup.string().max(100, getMaxErrStr(100)).nullable(),
  92. careOf: yup.string().max(60, getMaxErrStr(60)).nullable(),
  93. emailAddress: yup.string().email(intl.formatMessage({ id: 'validEmailFormat' })).max(255).test('checkEmailFormat', intl.formatMessage({ id: 'requireEmail' }), function (value) {
  94. if (isDummyLoggedIn()) {
  95. if (value !== undefined) {
  96. return true
  97. } else {
  98. return false
  99. }
  100. } else {
  101. return true
  102. }
  103. }),
  104. careOfDum: yup.string().max(60, getMaxErrStr(60)).test('checkCareOfDumFormat', intl.formatMessage({ id: 'requireCareOf' }), function (value) {
  105. if (isDummyLoggedIn()) {
  106. if (value !== undefined) {
  107. return true
  108. } else {
  109. return false
  110. }
  111. } else {
  112. return true
  113. }
  114. }),
  115. }),
  116. onSubmit: values => {
  117. if (!values.issueId) {
  118. setWarningTitle(intl.formatMessage({ id: "attention" }))
  119. setWarningText(intl.formatMessage({ id: 'requireTargetVol' }));
  120. setIsWarningPopUp(true);
  121. return;
  122. }
  123. if (!attachment) {
  124. setWarningTitle(intl.formatMessage({ id: "attention" }))
  125. setWarningText(intl.formatMessage({ id: 'requireFile' }));
  126. setIsWarningPopUp(true);
  127. return;
  128. } else if (!attachment.size || attachment.size <= 0) {
  129. setWarningTitle(intl.formatMessage({ id: "attention" }))
  130. setWarningText(intl.formatMessage({ id: 'requireValidFile' }));
  131. setIsWarningPopUp(true);
  132. return;
  133. } else if (attachment.size >= (10 * 1024 * 1034)) {
  134. setWarningTitle(intl.formatMessage({ id: "attention" }))
  135. setWarningText(intl.formatMessage({ id: 'fileSizeWarning' }));
  136. setIsWarningPopUp(true);
  137. return;
  138. }
  139. if (isORGLoggedIn()) {
  140. HttpUtils.get({
  141. url: UrlUtils.CHECK_OVERDUE,
  142. onSuccess: (responData) => {
  143. if (responData.haveOverdue) {
  144. setVal(values);
  145. setWarningTitle(intl.formatMessage({ id: "attention" }))
  146. setWarningText(intl.formatMessage({ id: 'dnOverdueWarning' }));
  147. setIsWarningPopUp(true);
  148. } else {
  149. apply(values);
  150. }
  151. }
  152. });
  153. } else {
  154. apply(values);
  155. }
  156. }
  157. });
  158. const apply = (values) => {
  159. setSubmitting(true)
  160. let careOf = values.careOf ?? "";
  161. let remarks = values.remarks ?? "";
  162. if (isDummyLoggedIn()) {
  163. careOf = values.careOfDum
  164. }
  165. if (isDummyLoggedIn()) {
  166. remarks = values.emailAddress
  167. }
  168. HttpUtils.postWithFiles({
  169. url: UrlUtils.POST_PUBLIC_NOTICE_APPLY,
  170. params: {
  171. id: 0,
  172. contactPerson: values.contactPerson,
  173. contactTelNo: {
  174. countryCode: values.tel_countryCode,
  175. phoneNumber: values.phoneNumber
  176. },
  177. contactFaxNo: {
  178. countryCode: values.fax_countryCode,
  179. faxNumber: values.faxNumber
  180. },
  181. issueId: issueId,
  182. careOf: careOf,
  183. remarks: remarks,
  184. },
  185. files: [attachment],
  186. onSuccess: function (responData) {
  187. if (responData.msg) {
  188. setVal({});
  189. setReloadPage(true);
  190. setWarningTitle(intl.formatMessage({ id: "attention" }))
  191. setWarningText(intl.formatMessage({ id: responData.msg }));
  192. setIsWarningPopUp(true);
  193. return;
  194. }
  195. setSubmitting(false)
  196. notifyActionSuccess(intl.formatMessage({ id: 'submissionSuccess' }) + '!')
  197. navigate("/publicNotice");
  198. // location.reload();
  199. }
  200. });
  201. }
  202. const readFile = (event) => {
  203. let file = event.target.files[0];
  204. if (file) {
  205. if (file.name.toLowerCase().substr(file.name.length - 4).includes(".doc")
  206. || file.name.toLowerCase().substr(file.name.length - 5).includes(".docx")
  207. || file.name.toLowerCase().substr(file.name.length - 4).includes(".xls")
  208. || file.name.toLowerCase().substr(file.name.length - 5).includes(".xlsx")
  209. ) {
  210. setAttachment(event.target.files[0]);
  211. } else {
  212. setWarningTitle(intl.formatMessage({ id: "attention" }))
  213. setWarningText(intl.formatMessage({ id: 'requireValidFileWithFormat' }));
  214. setIsWarningPopUp(true);
  215. setAttachment({});
  216. document.getElementById("uploadFileBtn").value = "";
  217. return;
  218. }
  219. }
  220. }
  221. return (
  222. <Grid container sx={{ minHeight: '87vh', backgroundColor: '#ffffff', mb: 3 }} direction="column" alignItems="center">
  223. <Grid item xs={12} md={12} width="100%" >
  224. <div style={BackgroundHead}>
  225. <Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
  226. <Typography ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}>
  227. <FormattedMessage id="applyPublicNotice" />
  228. </Typography>
  229. </Stack>
  230. </div>
  231. </Grid>
  232. <Grid item xs={12} width={{ xs: "90%", sm: "90%", md: "60%", lg: "60%" }}>
  233. <Button
  234. aria-label={intl.formatMessage({ id: 'back' })}
  235. title={intl.formatMessage({ id: 'back' })}
  236. sx={{ ml: 0, mt: 2.5 }} style={{ border: '2px solid' }} variant="outlined" onClick={() => { navigate(-1) }}
  237. >
  238. <ForwardIcon style={{ height: 30, width: 50, transform: "rotate(180deg)" }} />
  239. </Button>
  240. </Grid>
  241. {/* <Grid item xs={12}>
  242. <Typography variant="pnspsFormParagraphBold">申請公共啟事</Typography>
  243. </Grid> */}
  244. {
  245. isSubmitting ?
  246. <Grid item xs={12} md={12} width={{ md: "60%", xs: "90%" }}>
  247. <LoadingComponent />
  248. </Grid>
  249. :
  250. <Grid item xs={12} md={12} width={{ md: "60%", xs: "90%" }}>
  251. <Box xs={12} mt={1} sx={{ p: 2, border: '3px groove grey', borderRadius: '10px' }}>
  252. <form onSubmit={formik.handleSubmit}>
  253. <Grid container spacing={1} sx={{ minHeight: '80vh' }} direction="row" justifyContent="flex-start" alignItems="center">
  254. <Grid item xs={12} md={12} lg={12} sx={{ mb: 1 }}>
  255. {FieldUtils.getTextField({
  256. label: intl.formatMessage({ id: 'contactPerson' }) + ":",
  257. valueName: "contactPerson",
  258. form: formik,
  259. disabled: !isDummyLoggedIn(),
  260. autoFocus: isDummyLoggedIn()
  261. })}
  262. </Grid>
  263. <Grid item xs={12} md={12}>
  264. {FieldUtils.getPhoneField({
  265. label: intl.formatMessage({ id: 'userContactNumber' }) + ":",
  266. disabled: !isDummyLoggedIn(),
  267. valueName: {
  268. code: "tel_countryCode",
  269. num: "phoneNumber",
  270. },
  271. form: formik
  272. })}
  273. </Grid>
  274. <Grid item xs={12} md={12}>
  275. {FieldUtils.getPhoneField({
  276. label: intl.formatMessage({ id: 'contactFaxNumber' }) + ":",
  277. disabled: !isDummyLoggedIn(),
  278. valueName: {
  279. code: "fax_countryCode",
  280. num: "faxNumber",
  281. },
  282. form: formik
  283. })}
  284. </Grid>
  285. <Grid item xs={12} lg={12}>
  286. <Grid container alignItems={"center"}>
  287. <Grid item xs={12} md={3} lg={3}
  288. sx={{ display: 'flex', alignItems: 'center' }}>
  289. <Typography variant="pnspsFormParagraphBold">
  290. <FormattedMessage id="targetVol" />:
  291. </Typography>
  292. </Grid>
  293. <Grid item xs={12} md={9} lg={6}>
  294. <RadioGroup
  295. aria-labelledby="radio-buttons-group-label"
  296. id="issueId"
  297. name="issueId"
  298. defaultValue={issueId}
  299. onChange={(event) => {
  300. setIssueId(event.target.value);
  301. }}
  302. >
  303. {
  304. selections
  305. }
  306. </RadioGroup>
  307. </Grid>
  308. </Grid>
  309. </Grid>
  310. <Grid item xs={12} alignItems={"center"} sx={{ p: 2 }}>
  311. <table style={tabelStyle}>
  312. <tbody>
  313. <tr style={tabelStyle}>
  314. <th style={tabelStyle} width="400" align="left"><FormattedMessage id="paymentMeans" /></th>
  315. <th style={tabelStyle} width="300" align="left"><FormattedMessage id="confirmingDealine" /></th>
  316. <th style={tabelStyle} width="300" align="left"><FormattedMessage id="PaymentCoonpletDealine" /></th>
  317. </tr>
  318. <tr>
  319. <td style={tabelStyle}>
  320. <FormattedMessage id="payOnline" />
  321. <br /><a href="#payOnlineDetails" color='#fff' onClick={() => {
  322. setWarningTitle(intl.formatMessage({ id: "payOnline" }))
  323. setWarningText(
  324. <><FormattedMessage id="paymentMethodMeans" />
  325. <ul>
  326. <li><FormattedMessage id="fps" /></li>
  327. <li><FormattedMessage id="card" /></li>
  328. <li><FormattedMessage id="pps" /></li>
  329. </ul>
  330. </>
  331. );
  332. setIsWarningPopUp(true);
  333. }}><FormattedMessage id="viewDetail" /></a>
  334. </td>
  335. <td style={tabelStyle}>{DateUtils.dateFormat(closeDate, dft)} 2:00 p.m.</td>
  336. <td style={tabelStyle}>
  337. <FormattedMessage id="payOnlineRemark" values={{
  338. date: DateUtils.dateFormat(closeDate, dft) + ' 2:30 p.m.'
  339. }} />
  340. </td>
  341. </tr>
  342. <tr>
  343. <td style={tabelStyle}><FormattedMessage id="payDn" />
  344. <br /><a href="#payDnDetails" onClick={() => {
  345. setWarningTitle(intl.formatMessage({ id: "payDn" }))
  346. setWarningText(
  347. <><FormattedMessage id="paymentMethodMeans" />
  348. <ul>
  349. <li><FormattedMessage id="atm" /></li>
  350. <li><FormattedMessage id="pps" /></li>
  351. <li><FormattedMessage id="eBank" /></li>
  352. <li><FormattedMessage id="phoneBank" /></li>
  353. <li><FormattedMessage id="eCheque" /></li>
  354. <li><FormattedMessage id="fps" /></li>
  355. <li><FormattedMessage id="hkpo" /></li>
  356. <li><FormattedMessage id="store" /></li>
  357. <li><FormattedMessage id="post" /></li>
  358. </ul>
  359. <Typography variant="h6">
  360. <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "proofNote" }) }} />
  361. </Typography>
  362. </>
  363. );
  364. setIsWarningPopUp(true);
  365. }}><FormattedMessage id="viewDetail" /></a>
  366. </td>
  367. <td style={tabelStyle}>{DateUtils.dateFormat(closingDateOff, dft)} 5:00 p.m.</td>
  368. <td style={tabelStyle}>
  369. <FormattedMessage id="payDnRemark" values={{
  370. date: DateUtils.dateFormat(closeDate, dft) + " 12:30 p.m."
  371. }} />
  372. </td>
  373. </tr>
  374. <tr>
  375. <td style={tabelStyle}><FormattedMessage id="payNPGO" />
  376. <br /><a href="#payNPGODetails" onClick={() => {
  377. setWarningTitle(intl.formatMessage({ id: "payNPGO" }))
  378. setWarningText(
  379. <><FormattedMessage id="paymentMethodMeans" />
  380. <ul>
  381. <li><FormattedMessage id="cheque" /></li>
  382. <li><FormattedMessage id="cash" /></li>
  383. </ul>
  384. </>
  385. );
  386. setIsWarningPopUp(true);
  387. }}><FormattedMessage id="viewDetail" /></a>
  388. </td>
  389. <td style={tabelStyle}>{DateUtils.dateFormat(closeDate, dft)} 12:00 p.m.</td>
  390. <td style={tabelStyle}>
  391. <FormattedMessage id="payNPGORemark" values={{
  392. date: DateUtils.dateFormat(closeDate, dft) + " 12:30 p.m."
  393. }} />
  394. </td>
  395. </tr>
  396. </tbody>
  397. </table>
  398. </Grid>
  399. <Grid item xs={12} md={12} lg={12}>
  400. <Grid container direction="row" justifyContent="flex-start" alignItems="center">
  401. <Grid item xs={12} md={3} lg={3}
  402. sx={{ display: 'flex', alignItems: 'center' }}>
  403. <Typography variant="pnspsFormParagraphBold">
  404. <FormattedMessage id="draftFile" /> ({intl.formatMessage({ id: 'fileSizeWarning' })}):
  405. </Typography>
  406. </Grid>
  407. <Grid item xs={12} md={6} lg={6} sx={{ wordBreak: 'break-word' }}>
  408. <input
  409. id="uploadFileBtn"
  410. name="file"
  411. type="file"
  412. accept=".doc,.docx,.xls,.xlsx"
  413. style={{ display: 'none' }}
  414. onChange={(event) => {
  415. readFile(event)
  416. }}
  417. />
  418. {attachment.name}
  419. </Grid>
  420. {/* <Grid item xs={12} md={3} lg={3}>
  421. <label htmlFor="uploadFileBtn">
  422. <Button
  423. aria-label={intl.formatMessage({id: 'uploadFileBtn'})}
  424. component="span"
  425. variant="outlined"
  426. size="large"
  427. >{attachment ? intl.formatMessage({id: 'uploadFileBtn'}) : intl.formatMessage({id: 'reUpload'})}</Button>
  428. </label>
  429. </Grid> */}
  430. </Grid>
  431. </Grid>
  432. <Grid item xs={12} md={12} lg={12}>
  433. <Grid container direction="row" justifyContent="flex-start" alignItems="center">
  434. <Grid item xs={12} md={3} lg={3}
  435. sx={{ display: 'flex', alignItems: 'center' }}>
  436. </Grid>
  437. <Grid item xs={12} md={6} lg={6} >
  438. <label htmlFor="uploadFileBtn">
  439. <Button
  440. aria-label={intl.formatMessage({ id: 'uploadFileBtn' })}
  441. component="span"
  442. variant="outlined"
  443. size="large"
  444. >{attachment ? intl.formatMessage({ id: 'uploadFileBtn' }) : intl.formatMessage({ id: 'reUpload' })}</Button>
  445. </label>
  446. </Grid>
  447. <Grid item xs={12} md={3} lg={3}>
  448. </Grid>
  449. </Grid>
  450. </Grid>
  451. {isORGLoggedIn() ?
  452. <>
  453. {isDummyLoggedIn() ?
  454. <Grid item xs={12} md={12} lg={12}>
  455. {FieldUtils.getCarOfField({
  456. label: intl.formatMessage({ id: 'careOf' }) + ":",
  457. valueName: "careOfDum",
  458. form: formik,
  459. // disabled: true
  460. })}
  461. </Grid>
  462. :
  463. <Grid item xs={12} md={12} lg={12}>
  464. {FieldUtils.getCarOfField({
  465. label: intl.formatMessage({ id: 'careOf' }) + ":",
  466. valueName: "careOf",
  467. form: formik,
  468. // disabled: true
  469. })}
  470. </Grid>
  471. }
  472. <Grid item xs={12} md={12} lg={12} sx={{ mb: 3 }}>
  473. <Typography display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>
  474. <FormattedMessage id="noteOnClientRemark" />
  475. </Typography>
  476. </Grid>
  477. </>
  478. :
  479. null
  480. }
  481. {
  482. isDummyLoggedIn() ?
  483. <Grid item xs={12} md={12} lg={12}>
  484. {FieldUtils.getTextField({
  485. label: intl.formatMessage({ id: 'userContactEmail' }),
  486. valueName: "emailAddress",
  487. form: formik
  488. })}
  489. </Grid>
  490. :
  491. <Grid item xs={12} md={12} lg={12}>
  492. {FieldUtils.getTextArea({
  493. label: intl.formatMessage({ id: 'extraMark' }) + ":",
  494. valueName: "remarks",
  495. form: formik,
  496. inputProps: { maxLength: 255 }
  497. })}
  498. </Grid>
  499. }
  500. <Grid item xs={12}>
  501. <Typography variant="h6" height="100%" >
  502. <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "tradeMarkFootnote" }) }} />
  503. </Typography>
  504. </Grid>
  505. <Grid item xs={12}>
  506. <Stack direction="row">
  507. <Checkbox
  508. checked={tickAccept}
  509. onChange={(event) => {
  510. setTickAccept(event.target.checked)
  511. }}
  512. name="tickAccept"
  513. color="primary"
  514. size="small"
  515. />
  516. <Typography variant="h6" height="100%" >
  517. <div style={{ padding: 12 }} dangerouslySetInnerHTML={{ __html: intl.formatMessage({ id: "applyTickStr" }) }} />
  518. </Typography>
  519. </Stack>
  520. </Grid>
  521. <Grid item xs={12}>
  522. <center>
  523. <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}>
  524. <Button
  525. aria-label={intl.formatMessage({ id: 'applyPublicNotice' })}
  526. variant="contained"
  527. type="submit"
  528. disabled={!tickAccept}
  529. >
  530. <FormattedMessage id="applyPublicNotice" />
  531. </Button>
  532. </ThemeProvider>
  533. </center>
  534. </Grid>
  535. </Grid>
  536. </form>
  537. </Box>
  538. </Grid>
  539. }
  540. <div>
  541. <Dialog
  542. open={isWarningPopUp}
  543. onClose={() => setIsWarningPopUp(false)}
  544. PaperProps={{
  545. sx: {
  546. minWidth: '40vw',
  547. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '70vw' },
  548. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '60vh' }
  549. }
  550. }}
  551. >
  552. <DialogTitle>
  553. {warningTitle}
  554. </DialogTitle>
  555. <DialogContent style={{ display: 'flex', }}>
  556. <Typography variant="h5" style={{ padding: '16px' }}>
  557. {warningText}
  558. </Typography>
  559. </DialogContent>
  560. <DialogActions>
  561. <Button
  562. aria-label={intl.formatMessage({ id: 'ok' })}
  563. onClick={() => {
  564. if (val.contactPerson) {
  565. apply(val);
  566. setIsWarningPopUp(false);
  567. } else {
  568. setIsWarningPopUp(false);
  569. if (reloadPage) {
  570. location.reload();
  571. }
  572. }
  573. }}
  574. >
  575. <FormattedMessage id="ok" />
  576. </Button>
  577. </DialogActions>
  578. </Dialog>
  579. </div>
  580. </Grid>
  581. );
  582. };
  583. export default PublicNoticeApplyForm;