Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

546 linhas
28 KiB

  1. // material-ui
  2. import {
  3. Grid,
  4. Typography,
  5. Stack,
  6. Button,
  7. Box,
  8. } from '@mui/material';
  9. import * as React from "react";
  10. import * as HttpUtils from "utils/HttpUtils";
  11. import * as UrlUtils from "utils/ApiPathConst";
  12. import { useNavigate } from "react-router-dom";
  13. import FpsIcon from "assets/images/icons/fps.svg";
  14. import expiredQrcodeEN from "assets/images/icons/expiredQrcodeEN.png";
  15. import expiredQrcodeZH from "assets/images/icons/expiredQrcodeZH.png";
  16. import expiredQrcodeCN from "assets/images/icons/expiredQrcodeCN.png";
  17. import { useLocation } from 'react-router-dom';
  18. // import {paymentPath} from "auth/utils";
  19. import {currencyFormat} from "utils/FormatUtils";
  20. // import {poll} from "utils/Utils";
  21. import Loadable from 'components/Loadable';
  22. const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
  23. import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
  24. import {FormattedMessage, useIntl} from "react-intl";
  25. import { PRIMARY_CONTAINED_BUTTON_SX, ERROR_CONTAINED_BUTTON_SX } from "themes/colorConst";
  26. const BackgroundHead = {
  27. backgroundImage: `url(${titleBackgroundImg})`,
  28. width: '100%',
  29. height: '100%',
  30. backgroundSize: 'contain',
  31. backgroundRepeat: 'no-repeat',
  32. backgroundColor: '#0C489E',
  33. backgroundPosition: 'right'
  34. }
  35. // ==============================|| DASHBOARD - DEFAULT ||============================== //
  36. const Index = () => {
  37. const navigate = useNavigate()
  38. const location = useLocation();
  39. const intl = useIntl();
  40. const { locale } = intl;
  41. const [locationData, setLocationData] = React.useState({});
  42. const [paymentData, setPaymentData] = React.useState({});
  43. const [onReady, setOnReady] = React.useState(false);
  44. const [qrCodeTimeout, setqrCodeTimeout] = React.useState(false);
  45. const [paymentStatusCode, setPaymentStatusCode] = React.useState("");
  46. const [expiredQrcode, setExpiredQrcode] = React.useState(expiredQrcodeEN);
  47. const [responeData, setResponeDataData] = React.useState({});
  48. const [fpsTransctionData, setFpsTransctionData] = React.useState({});
  49. const [fpsmerchanttimeoutdatetime, setFpsmerchanttimeoutdatetime] = React.useState("");
  50. const [timeDownCount, setTimeDownCount] = React.useState(0);
  51. const [paymentId, setPaymentId] = React.useState("");
  52. const [fpsqrcodeurl, setFpsqrcodeurl] = React.useState("");
  53. const [fpsqrcodeurlPrd, setFpsqrcodeurlPrd] = React.useState("");
  54. const [fpsqrcodeurlFps, setFpsqrcodeurlFps] = React.useState("");
  55. const [browserType, setBrowserType] = React.useState("");
  56. const [sysEnv, setSysEnv] = React.useState("");
  57. const [isError, setIsError] = React.useState(false);
  58. const mobileBrowser = "Mobile";
  59. const desktopBrowser = "Desktop";
  60. const pasgPathPrd = 'https://fps.payapps.hkicl.com.hk'; //PRD
  61. const pasgPath = 'https://sim.fps.payapps.hkicl.com.hk'; //Testing
  62. // const loadPaymentUrl = "/api/payment/wallet/fps";
  63. // const cancelPaymentUrl = "/api/payment/cancelpayment";
  64. // const paymentStatusApi = "/api/payment/status/";
  65. // const payloadUrl = "/api/payment/wallet/fps/enquiryfpspayload/";
  66. // const receiverUrl = "/noti-api/payment/payment-notification";
  67. //timer
  68. const currentTimer = React.useRef();
  69. const [time, setTime] = React.useState(0);
  70. React.useEffect(() => {
  71. // console.log (location.state)
  72. if(Object.keys(location.state).length > 0){
  73. // console.log (location.state)
  74. setLocationData(location.state)
  75. setBrowserType(desktopBrowser)
  76. if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) {
  77. // console.log('Mobile web browser');
  78. setBrowserType(mobileBrowser)
  79. // setFpsqrcodeurl(openPASGUrl)
  80. } else {
  81. // console.log('Desktop web browser');
  82. setBrowserType(desktopBrowser)
  83. }
  84. }
  85. }, []);
  86. React.useEffect(() => {
  87. // console.log (locationData)
  88. if (Object.keys(locationData).length > 0){
  89. setPaymentData(locationData)
  90. // loadForm();
  91. }
  92. }, [locationData]);
  93. React.useEffect(() => {
  94. // console.log (locationData)
  95. if (locale === 'zh-HK'){
  96. setExpiredQrcode(expiredQrcodeZH)
  97. } else if (locale === 'en'){
  98. setExpiredQrcode(expiredQrcodeEN)
  99. } else {
  100. setExpiredQrcode(expiredQrcodeCN)
  101. }
  102. }, [locale]);
  103. React.useEffect(() => {
  104. // console.log (paymentData)
  105. if (Object.keys(paymentData).length > 0){
  106. loadForm();
  107. }
  108. }, [paymentData]);
  109. React.useEffect(() => {
  110. // console.log(responeData)
  111. if(Object.keys(responeData).length > 0 && fpsmerchanttimeoutdatetime!=""){
  112. setFpsTransctionData(responeData)
  113. }
  114. }, [responeData]);
  115. React.useEffect(() => {
  116. // console.log(fpsTransctionData)
  117. if(Object.keys(fpsTransctionData).length > 0 ){
  118. setPaymentId(fpsTransctionData.paymentid)
  119. currentTimer.current = setInterval(() => {
  120. setTime((prevTime) => prevTime + 1);
  121. }, 1000);
  122. setOnReady(true);
  123. return () => clearInterval(currentTimer.current);
  124. }
  125. }, [fpsTransctionData]);
  126. const loadForm = () => {
  127. const webtoken = paymentData.webtoken;
  128. const transactionid = paymentData.transactionid;
  129. console.log(webtoken)
  130. console.log(transactionid)
  131. localStorage.removeItem("transactionid")
  132. localStorage.removeItem("webtoken")
  133. localStorage.setItem("transactionid", transactionid)
  134. localStorage.setItem("webtoken", webtoken)
  135. HttpUtils.post({
  136. url: UrlUtils.LOAD_PAYMENT_FPS_URL,
  137. params:{
  138. "transactionid": transactionid,
  139. "webtoken": webtoken,
  140. "paymentmethod":"04,BCFP,FPS",
  141. "order": {
  142. "totalamount":paymentData.amount,
  143. "currency":"HKD",
  144. "orderdetail":
  145. [
  146. {
  147. "itemid": "1",
  148. "qty":"1",
  149. "unitprice":paymentData.amount,
  150. "amount":paymentData.amount
  151. },
  152. ]
  153. },
  154. // "locale":"<locale>",
  155. // "eserviceid":"<eserviceid>"
  156. },
  157. onSuccess: function(responseData){
  158. try {
  159. /*
  160. {
  161. "paymentid": "<paymentid>",
  162. "paymentstatus": "<paymentstatus>",
  163. "fpsmerchanttimeoutdatetime": <fpsmerchanttimeoutdatetime>,
  164. "fpsqrcodeimgbase64": "<fpsqrcodeimgbase64>",
  165. "fpsqrcodeurl": "<fpsqrcodeurl>"
  166. }
  167. */
  168. if (!responseData || !responseData.fpsqrcodeurl) {
  169. throw new Error("Missing expected response data");
  170. }
  171. setResponeDataData(responseData)
  172. const timeoutdatetime = responseData.fpsmerchanttimeoutdatetime
  173. setSysEnv(responseData.sysEnv)
  174. const searchString = "[UTC]";
  175. let convertedDateString = "";
  176. if ( timeoutdatetime.toString().includes(searchString) ){
  177. convertedDateString = timeoutdatetime.replace("[UTC]", "");
  178. } else {
  179. convertedDateString = timeoutdatetime;
  180. }
  181. setFpsmerchanttimeoutdatetime(convertedDateString)
  182. const parsedUrl = new URL(responseData.fpsqrcodeurl);
  183. const fpsqrcodeurl = "https://"+window.location.hostname+'/payment'+parsedUrl.pathname;
  184. const fpsqrcodeurlwithFps = "https://"+"fps."+window.location.hostname+parsedUrl.pathname;
  185. console.log(parsedUrl)
  186. console.log(fpsqrcodeurl)
  187. console.log(fpsqrcodeurlwithFps)
  188. const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  189. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  190. const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  191. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  192. const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  193. + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?&PAYMENT_ID='+localStorage.getItem("paymentId"));
  194. // const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  195. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  196. // const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  197. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  198. // const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  199. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  200. setFpsqrcodeurl(openPASGUrl)
  201. setFpsqrcodeurlPrd(openPASGUrlPrd)
  202. setFpsqrcodeurlFps(openPASGUrlPrdFps)
  203. } catch (e) {
  204. console.error("Error processing response:", e);
  205. setIsError(true);
  206. setOnReady(true);
  207. }
  208. },
  209. onFail: function(error){
  210. console.error("post onFail:", error);
  211. setIsError(true);
  212. setOnReady(true);
  213. },
  214. onError: function(error){
  215. console.error("post onError:", error);
  216. setIsError(true);
  217. setOnReady(true);
  218. }
  219. });
  220. //testing
  221. // const timeoutdatetime = "2024-11-18T07:04:35Z[UTC]"
  222. // const convertedDateString = timeoutdatetime.replace("[UTC]", "");
  223. // setFpsmerchanttimeoutdatetime(convertedDateString)
  224. // setPaymentId("C202310268000681")
  225. // const responseData=
  226. // {
  227. // "paymentid": "C202310268000681",
  228. // "paymentstatus": "INPR",
  229. // "fpsmerchanttimeoutdatetime": "2024-11-18T07:04:35Z[UTC]",
  230. // "fpsqrcodeimgbase64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAuyklEQVR4Xu3dfcy/d1nff2IAAAAASUVORK5CYII=",
  231. // "fpsqrcodeurl": "http://127.0.0.1:8080/api/payment/wallet/fps/enquiryfpspayload/vm.JKDDlTOavR3ASviSwUnS1Lw4-"
  232. // }
  233. // setResponeDataData(responseData)
  234. // const parsedUrl = new URL(responseData.fpsqrcodeurl);
  235. // const fpsqrcodeurl = "https://"+window.location.hostname+'/payment'+parsedUrl.pathname;
  236. // const fpsqrcodeurlwithFps = "https://"+"fps."+window.location.hostname+parsedUrl.pathname;
  237. // console.log(parsedUrl)
  238. // console.log(fpsqrcodeurl)
  239. // console.log(fpsqrcodeurlwithFps)
  240. // const openPASGUrl = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  241. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  242. // const openPASGUrlPrd = pasgPathPrd + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurl) + '&callback='
  243. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  244. // const openPASGUrlPrdFps = pasgPath + '?pay_req_obj=' + encodeURIComponent(fpsqrcodeurlwithFps) + '&callback='
  245. // + encodeURIComponent("https://"+window.location.hostname+ '/paymentPage/fps/fpscallback?TRANSACTION_ID='+transactionid+"&WEB_TOKEN="+webtoken+"&PAYMENT_ID="+localStorage.getItem("paymentId"));
  246. // setFpsqrcodeurl(openPASGUrl)
  247. // setFpsqrcodeurlPrd(openPASGUrlPrd)
  248. // setFpsqrcodeurlFps(openPASGUrlPrdFps)
  249. }
  250. const getPaymentStatus = () => {
  251. if(Object.keys(paymentData).length > 0){
  252. HttpUtils.post({
  253. url: UrlUtils.PAYMENT_STATUS_API,
  254. params:{
  255. "apprefid": paymentData.transactionid,
  256. // "webtoken": paymentData.webtoken,
  257. "transactionid":Number(paymentData.transactionid)
  258. },
  259. onSuccess: function(responseData){
  260. const paymentstatuscode = responseData.paymentdetail.result.paymentstatuscode;
  261. setPaymentStatusCode(paymentstatuscode)
  262. if (paymentstatuscode != "" && paymentstatuscode != "INPR" ){
  263. if (paymentstatuscode === 'APPR') {
  264. // const timestamp = Date.now();
  265. let page = '/paymentPage/fps/ackpage';
  266. let stateParams = { state: { transactionid: paymentData.transactionid} }
  267. navigate(page, stateParams);
  268. } else if (paymentstatuscode === 'CANC') {
  269. // const timestamp = Date.now();
  270. let page = '/paymentPage/fps/ackpage';
  271. let stateParams = { state: { transactionid: paymentData.transactionid} }
  272. navigate(page, stateParams);
  273. } else {
  274. // window.top.location.href = paymentPath + payment.config.errPagePath;
  275. alert("ERROR")
  276. }
  277. }
  278. },
  279. onError: function(){
  280. alert("ERROR")
  281. // cancelPayment()
  282. // clearInterval(currentTimer.current);
  283. }
  284. });
  285. }
  286. };
  287. React.useEffect(() => {
  288. const timeOutDate = new Date(fpsmerchanttimeoutdatetime);
  289. const currentTime = new Date;
  290. const timedowncount = Math.round((timeOutDate.getTime() - currentTime.getTime()) / 1000);
  291. // console.log(time)
  292. // console.log(timeOutDate)
  293. // console.log(currentTime)
  294. // console.log(timeOutDate.getTime()-currentTime.getTime())
  295. if (browserType === desktopBrowser){
  296. getPaymentStatus();
  297. if (timeOutDate.getTime()<currentTime.getTime()){
  298. // console.log("stop");
  299. clearInterval(currentTimer.current);
  300. setqrCodeTimeout(true)
  301. setTimeDownCount(0);
  302. // cancelPayment()
  303. }else{
  304. setTimeDownCount(timedowncount);
  305. }
  306. }
  307. },[time])
  308. const cancelPayment = ()=>{
  309. if (Object.keys(paymentData).length>0){
  310. getPaymentStatus()
  311. if (paymentStatusCode === "INPR" || isError || paymentStatusCode === ""){
  312. HttpUtils.post({
  313. url: UrlUtils.CANCEL_PAYMENT_URL,
  314. params:{
  315. "transactionid": paymentData.transactionid,
  316. "webtoken": paymentData.webtoken,
  317. "paymentid": fpsTransctionData.paymentid || localStorage.getItem("paymentId")
  318. },
  319. onSuccess: function(){
  320. navigate('/paymentPage/fps/ackpage');
  321. let page = '/paymentPage/fps/ackpage';
  322. let stateParams = { state: { transactionid: paymentData.transactionid} }
  323. navigate(page, stateParams);
  324. },
  325. onFail: function(){
  326. let page = '/paymentPage/fps/ackpage';
  327. let stateParams = { state: { transactionid: paymentData.transactionid} }
  328. navigate(page, stateParams);
  329. },
  330. onError: function(){
  331. let page = '/paymentPage/fps/ackpage';
  332. let stateParams = { state: { transactionid: paymentData.transactionid} }
  333. navigate(page, stateParams);
  334. }
  335. });
  336. }
  337. }
  338. }
  339. const mobliePayment = ()=>{
  340. window.location.assign(fpsqrcodeurl);
  341. }
  342. const mobliePaymentPrd = ()=>{
  343. window.location.assign(fpsqrcodeurlPrd);
  344. }
  345. const mobliePaymentFps = ()=>{
  346. window.location.assign(fpsqrcodeurlFps);
  347. }
  348. return (
  349. !onReady ?
  350. <Grid container sx={{ minHeight: '87vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center">
  351. <Grid item>
  352. <LoadingComponent />
  353. </Grid>
  354. </Grid>
  355. :
  356. (
  357. <Grid container sx={{ minHeight: '110vh', backgroundColor: '#fff' }} direction="column" justifyContent="flex-start" alignItems="center" >
  358. <Grid item xs={12} width="100%">
  359. <div style={BackgroundHead} width="100%">
  360. <Stack direction="row" height='70px'>
  361. <Typography ml={15} color='#FFF' variant="h4" sx={{ pt: 2 }}>
  362. <FormattedMessage id="publicNoticePaymentFPSPay"/>
  363. </Typography>
  364. </Stack>
  365. </div>
  366. </Grid>
  367. {/*row 1*/}
  368. <Grid item xs={12} md={12} width="100%">
  369. <Grid container justifyContent="flex-start" alignItems="center" width="100%">
  370. <center style={{ width: '100%', display: 'block' }}>
  371. <Grid item xs={12} md={12} >
  372. <Box sx={{ width: { xs: '90%', md: '40%' }, mx: 'auto', mt: 4, textAlign: 'left' }}>
  373. {!isError &&
  374. <Typography component="div" variant="body1" color="error" sx={{ fontSize: '20px', fontWeight: 700, textAlign: 'center' }}>
  375. <FormattedMessage id="payAlert"/>
  376. </Typography>
  377. }
  378. {isError &&
  379. <Typography component="div" variant="body1" color="error" sx={{ fontSize: '20px', fontWeight: 700, textAlign: 'justify' }}>
  380. <FormattedMessage id="fpsPaymentErrorMsg1"/>
  381. <Box
  382. component="ul"
  383. sx={{
  384. m: 0,
  385. mt: 1,
  386. px: 0,
  387. width: '100%',
  388. boxSizing: 'border-box',
  389. listStylePosition: 'outside',
  390. paddingLeft: '1.5em',
  391. textAlign: 'justify',
  392. }}
  393. >
  394. <li style={{fontWeight:'bold', textAlign: 'justify'}}><FormattedMessage id="fpsPaymentErrorMsg2"/></li>
  395. <li style={{fontWeight:'bold', textAlign: 'justify'}}><FormattedMessage id="fpsPaymentErrorMsg3"/></li>
  396. </Box>
  397. </Typography>
  398. }
  399. </Box>
  400. <Typography component="span" variant="body1" sx={{ width: '80%', margin: 'auto', mt: 4, textAlign: "center", fontSize: '20px', display: 'block' }}>
  401. <img src={FpsIcon} width="80" height="80" alt="FPS"></img>
  402. <br />
  403. <FormattedMessage id="payTotalDeatail"/>
  404. <br />
  405. {"HK$ " + currencyFormat(paymentData.amount)}
  406. </Typography>
  407. <br />
  408. {!isError &&
  409. (browserType==mobileBrowser?
  410. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  411. {
  412. sysEnv=="prod"?
  413. <Button
  414. component="span"
  415. variant="contained"
  416. size="large"
  417. color="primary"
  418. onClick={()=>{
  419. mobliePaymentPrd();
  420. }}
  421. sx={{ m: 4 }}
  422. ><FormattedMessage id="fpsSelectPaymentApp"/></Button>
  423. :
  424. <>
  425. <Button
  426. component="span"
  427. variant="contained"
  428. size="large"
  429. color="primary"
  430. onClick={()=>{
  431. mobliePayment();
  432. }}
  433. sx={{ m: 4 }}
  434. ><FormattedMessage id="fpsSelectPaymentApp"/>-Testing</Button>
  435. <Button
  436. component="span"
  437. variant="contained"
  438. size="large"
  439. color="primary"
  440. onClick={()=>{
  441. mobliePaymentFps();
  442. }}
  443. sx={{ m: 4 }}
  444. ><FormattedMessage id="fpsSelectPaymentApp"/>-fps prefix</Button>
  445. </>
  446. }
  447. </Typography>
  448. :
  449. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  450. <FormattedMessage id="fpsQrcodeTitle1"/>
  451. <br />
  452. {
  453. !qrCodeTimeout?
  454. <img src={fpsTransctionData.fpsqrcodeimgbase64} alt="QR Code"/>
  455. :<img src={expiredQrcode} alt="Expired QR Code"/>
  456. }
  457. <br />
  458. {"["+paymentId+"]"}
  459. <br/>
  460. {
  461. timeDownCount<=0?
  462. <FormattedMessage id="fpsQrcodeExpired"/>:
  463. <>
  464. <FormattedMessage id="fpsQrcodeTitle2"/>
  465. <br />
  466. <FormattedMessage id="fpsQrcodeTitle3"/>
  467. <br />
  468. <FormattedMessage id="fpsQrcodeTitle4"/>&nbsp;
  469. {timeDownCount}&nbsp;
  470. <FormattedMessage id="fpsQrcodeTitle5"/>
  471. </>
  472. }
  473. </Typography>
  474. )}
  475. <Typography variant="h3" sx={{ ml: 8, mt: 4, mr: 8, textAlign: "center" }}>
  476. {isError ?
  477. <Button
  478. component="span"
  479. variant="contained"
  480. size="large"
  481. color="primary"
  482. onClick={() => navigate("/dashboard")}
  483. sx={{ m: 4, ...PRIMARY_CONTAINED_BUTTON_SX }}
  484. >
  485. <FormattedMessage id="backToMainPage"/>
  486. </Button>
  487. :
  488. <Button
  489. component="span"
  490. variant="contained"
  491. size="large"
  492. onClick={()=>{
  493. cancelPayment();
  494. }}
  495. sx={{ m: 4, ...ERROR_CONTAINED_BUTTON_SX }}
  496. >
  497. <FormattedMessage id="payCancel"/>
  498. </Button>
  499. }
  500. </Typography>
  501. </Grid>
  502. </center>
  503. </Grid>
  504. </Grid>
  505. {/*row 2*/}
  506. </Grid >
  507. )
  508. );
  509. };
  510. export default Index;