| @@ -2,24 +2,22 @@ | |||||
| import Routes from 'routes'; | import Routes from 'routes'; | ||||
| import ThemeCustomization from 'themes'; | import ThemeCustomization from 'themes'; | ||||
| import ScrollTop from 'components/ScrollTop'; | import ScrollTop from 'components/ScrollTop'; | ||||
| import {ToastContainer} from "react-toastify"; | |||||
| import 'react-toastify/dist/ReactToastify.css'; | |||||
| import {PNSPS_THEME} from "./themes/themeConst"; | |||||
| import {ThemeProvider} from "@emotion/react"; | |||||
| import { PNSPS_THEME } from './themes/themeConst'; | |||||
| import { ThemeProvider } from '@emotion/react'; | |||||
| import GlobalNotificationModal from 'components/GlobalNotificationModal'; | |||||
| //import {isUserLoggedIn} from 'utils/Utils'; | //import {isUserLoggedIn} from 'utils/Utils'; | ||||
| //import {DefaultRoute} from 'routes/index' | //import {DefaultRoute} from 'routes/index' | ||||
| // ==============================|| APP - THEME, ROUTER, LOCAL ||============================== // | // ==============================|| APP - THEME, ROUTER, LOCAL ||============================== // | ||||
| const App = () => ( | const App = () => ( | ||||
| <ThemeCustomization> | |||||
| <ThemeProvider theme={PNSPS_THEME}> | |||||
| <ScrollTop> | |||||
| <Routes> | |||||
| </Routes> | |||||
| </ScrollTop> | |||||
| </ThemeProvider> | |||||
| <ToastContainer/> | |||||
| </ThemeCustomization> | |||||
| <ThemeCustomization> | |||||
| <ThemeProvider theme={PNSPS_THEME}> | |||||
| <ScrollTop> | |||||
| <Routes /> | |||||
| </ScrollTop> | |||||
| <GlobalNotificationModal /> | |||||
| </ThemeProvider> | |||||
| </ThemeCustomization> | |||||
| ); | ); | ||||
| export default App; | export default App; | ||||
| @@ -0,0 +1,71 @@ | |||||
| import React, { useEffect, useState } from 'react'; | |||||
| import Dialog from '@mui/material/Dialog'; | |||||
| import DialogTitle from '@mui/material/DialogTitle'; | |||||
| import DialogContent from '@mui/material/DialogContent'; | |||||
| import DialogContentText from '@mui/material/DialogContentText'; | |||||
| import DialogActions from '@mui/material/DialogActions'; | |||||
| import { Button } from '@mui/material'; | |||||
| import { useIntl } from 'react-intl'; | |||||
| const EVENT_NAME = 'pnsps-notification'; | |||||
| const typeToTitleId = { | |||||
| success: 'success', | |||||
| error: 'error', | |||||
| warning: 'warning' | |||||
| }; | |||||
| export default function GlobalNotificationModal() { | |||||
| const intl = useIntl(); | |||||
| const [open, setOpen] = useState(false); | |||||
| const [message, setMessage] = useState(''); | |||||
| const [type, setType] = useState('success'); | |||||
| useEffect(() => { | |||||
| const handler = (event) => { | |||||
| const { type: incomingType, message: incomingMessage } = event.detail || {}; | |||||
| if (!incomingMessage) return; | |||||
| setType(incomingType || 'success'); | |||||
| setMessage(incomingMessage); | |||||
| setOpen(true); | |||||
| }; | |||||
| window.addEventListener(EVENT_NAME, handler); | |||||
| return () => { | |||||
| window.removeEventListener(EVENT_NAME, handler); | |||||
| }; | |||||
| }, []); | |||||
| const handleClose = () => { | |||||
| setOpen(false); | |||||
| }; | |||||
| return ( | |||||
| <Dialog | |||||
| open={open} | |||||
| fullWidth | |||||
| maxWidth="sm" | |||||
| onClose={handleClose} | |||||
| aria-labelledby="global-notification-title" | |||||
| aria-describedby="global-notification-description" | |||||
| > | |||||
| <DialogTitle id="global-notification-title"> | |||||
| {intl.formatMessage({ id: typeToTitleId[type] || typeToTitleId.success })} | |||||
| </DialogTitle> | |||||
| <DialogContent> | |||||
| <DialogContentText | |||||
| id="global-notification-description" | |||||
| sx={{ color: 'black' }} | |||||
| > | |||||
| {message} | |||||
| </DialogContentText> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={handleClose} autoFocus> | |||||
| {intl.formatMessage({ id: 'close' })} | |||||
| </Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| ); | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| import { useEffect, useState } from 'react'; | |||||
| import { useEffect, useState, useRef } from 'react'; | |||||
| import { | import { | ||||
| Box, | Box, | ||||
| @@ -101,6 +101,7 @@ const CustomFormWizard = (props) => { | |||||
| const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); | const [termsAndConNotAccept, setTermsAndConNotAccept] = useState(false); | ||||
| const [isValid, setisValid] = useState(false); | const [isValid, setisValid] = useState(false); | ||||
| const [checkCountry, setCheckCountry] = useState(false); | const [checkCountry, setCheckCountry] = useState(false); | ||||
| const fileInputRef = useRef(null); | |||||
| const username = document.getElementById("username-login") | const username = document.getElementById("username-login") | ||||
| const [checkUsername, setCheckUsername] = useState(false); | const [checkUsername, setCheckUsername] = useState(false); | ||||
| @@ -1817,18 +1818,36 @@ const CustomFormWizard = (props) => { | |||||
| </Typography> | </Typography> | ||||
| <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}> | <Stack mt={1} direction="row" justifyContent="flex-start" alignItems="center" spacing={2}> | ||||
| <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | <ThemeProvider theme={PNSPS_LONG_BUTTON_THEME}> | ||||
| <Button variant="contained" component="label" sx={{ height: '40px' }}> | |||||
| <Button | |||||
| variant="contained" | |||||
| sx={{ height: '40px' }} | |||||
| type="button" | |||||
| onClick={() => { | |||||
| if (fileInputRef.current) { | |||||
| fileInputRef.current.click(); | |||||
| } | |||||
| }} | |||||
| onKeyDown={(event) => { | |||||
| if (event.key === 'Enter' || event.key === ' ') { | |||||
| event.preventDefault(); | |||||
| if (fileInputRef.current) { | |||||
| fileInputRef.current.click(); | |||||
| } | |||||
| } | |||||
| }} | |||||
| > | |||||
| <FormattedMessage id="uploadIdDoc" /> | <FormattedMessage id="uploadIdDoc" /> | ||||
| <input | |||||
| accept="image/png, .jpg, .bmp, .pdf" | |||||
| //className={classes.input} | |||||
| id="contained-button-file" | |||||
| multiple | |||||
| type="file" | |||||
| onChange={handleFileUpload} | |||||
| style={{ display: 'none' }} | |||||
| /> | |||||
| </Button> | </Button> | ||||
| <input | |||||
| accept="image/png, .jpg, .bmp, .pdf" | |||||
| //className={classes.input} | |||||
| id="contained-button-file" | |||||
| multiple | |||||
| type="file" | |||||
| onChange={handleFileUpload} | |||||
| ref={fileInputRef} | |||||
| style={{ display: 'none' }} | |||||
| /> | |||||
| </ThemeProvider> | </ThemeProvider> | ||||
| {/*<Typography xs={12} sm={9} md={3} display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>如: 香港身份證; 護照; 中國內地身份證等</Typography>*/} | {/*<Typography xs={12} sm={9} md={3} display="inline" variant="subtitle1" sx={{ color: 'primary.primary' }}>如: 香港身份證; 護照; 中國內地身份證等</Typography>*/} | ||||
| </Stack> | </Stack> | ||||
| @@ -371,6 +371,7 @@ | |||||
| "to": "To", | "to": "To", | ||||
| "all": "All", | "all": "All", | ||||
| "success": "Success", | "success": "Success", | ||||
| "error": "Error", | |||||
| "reject": "Reject", | "reject": "Reject", | ||||
| "cancelledStatus": "Cancelled", | "cancelledStatus": "Cancelled", | ||||
| "inProgress": "In Progress", | "inProgress": "In Progress", | ||||
| @@ -403,6 +403,7 @@ | |||||
| "to": "到", | "to": "到", | ||||
| "all": "全部", | "all": "全部", | ||||
| "success": "成功", | "success": "成功", | ||||
| "error": "错误", | |||||
| "reject": "拒绝", | "reject": "拒绝", | ||||
| "cancelledStatus": "取消", | "cancelledStatus": "取消", | ||||
| "inProgress": "进行中", | "inProgress": "进行中", | ||||
| @@ -404,6 +404,7 @@ | |||||
| "to": "到", | "to": "到", | ||||
| "all": "全部", | "all": "全部", | ||||
| "success": "成功", | "success": "成功", | ||||
| "error": "錯誤", | |||||
| "reject": "拒絕", | "reject": "拒絕", | ||||
| "cancelledStatus": "取消", | "cancelledStatus": "取消", | ||||
| "inProgress": "進行中", | "inProgress": "進行中", | ||||
| @@ -6,7 +6,6 @@ import DialogActions from "@mui/material/DialogActions"; | |||||
| import { Button } from "@mui/material"; | import { Button } from "@mui/material"; | ||||
| import Dialog from "@mui/material/Dialog"; | import Dialog from "@mui/material/Dialog"; | ||||
| import * as React from "react"; | import * as React from "react"; | ||||
| import { toast } from "react-toastify"; | |||||
| export const clickableLink=(link, label)=> { | export const clickableLink=(link, label)=> { | ||||
| return <a href={link}>{label}</a>; | return <a href={link}>{label}</a>; | ||||
| @@ -83,152 +82,71 @@ export function getDateString(queryDateArray) { | |||||
| ) | ) | ||||
| } | } | ||||
| const NOTIFICATION_EVENT_NAME = "pnsps-notification"; | |||||
| const dispatchNotification = (type, message) => { | |||||
| window.dispatchEvent( | |||||
| new CustomEvent(NOTIFICATION_EVENT_NAME, { | |||||
| detail: { | |||||
| type, | |||||
| message | |||||
| } | |||||
| }) | |||||
| ); | |||||
| }; | |||||
| export const notifySaveSuccess = () => { | export const notifySaveSuccess = () => { | ||||
| const userType = JSON.parse(localStorage.getItem("userData")).type | |||||
| toast.success(userType === "GLD" ? 'Save success!' : "儲存成功!", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| const userType = JSON.parse(localStorage.getItem("userData")).type; | |||||
| const msg = userType === "GLD" ? "Save success!" : "儲存成功!"; | |||||
| dispatchNotification("success", msg); | |||||
| }; | }; | ||||
| export const notifyCreateSuccess = () => { | export const notifyCreateSuccess = () => { | ||||
| const userType = JSON.parse(localStorage.getItem("userData")).type | |||||
| toast.success(userType === "GLD" ? 'Create success!' : "創建成功!", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| const userType = JSON.parse(localStorage.getItem("userData")).type; | |||||
| const msg = userType === "GLD" ? "Create success!" : "創建成功!"; | |||||
| dispatchNotification("success", msg); | |||||
| }; | }; | ||||
| export const notifyVerifySuccess = () => { | export const notifyVerifySuccess = () => { | ||||
| const userType = JSON.parse(localStorage.getItem("userData")).type | |||||
| toast.success(userType === "GLD" ? 'Verify success!' : "驗證成功!", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| const userType = JSON.parse(localStorage.getItem("userData")).type; | |||||
| const msg = userType === "GLD" ? "Verify success!" : "驗證成功!"; | |||||
| dispatchNotification("success", msg); | |||||
| }; | }; | ||||
| export const notifyDeleteSuccess = () => { | export const notifyDeleteSuccess = () => { | ||||
| const userType = JSON.parse(localStorage.getItem("userData")).type | |||||
| toast.success(userType === "GLD" ? 'Delete success!' : "刪除成功!", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| const userType = JSON.parse(localStorage.getItem("userData")).type; | |||||
| const msg = userType === "GLD" ? "Delete success!" : "刪除成功!"; | |||||
| dispatchNotification("success", msg); | |||||
| }; | }; | ||||
| export const notifyLockSuccess = () => { | export const notifyLockSuccess = () => { | ||||
| toast.success('Lock success!', { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("success", "Lock success!"); | |||||
| }; | }; | ||||
| export const notifyUnlockSuccess = () => { | export const notifyUnlockSuccess = () => { | ||||
| toast.success('Unlock success!', { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("success", "Unlock success!"); | |||||
| }; | }; | ||||
| export const notifyActiveSuccess = () => { | export const notifyActiveSuccess = () => { | ||||
| toast.success('Active success!', { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("success", "Active success!"); | |||||
| }; | }; | ||||
| export const notifyDownloadSuccess = () => { | export const notifyDownloadSuccess = () => { | ||||
| const userType = JSON.parse(localStorage.getItem("userData")).type | |||||
| toast.success(userType === "GLD" ? 'Download success!' : "下載成功!", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| const userType = JSON.parse(localStorage.getItem("userData")).type; | |||||
| const msg = userType === "GLD" ? "Download success!" : "下載成功!"; | |||||
| dispatchNotification("success", msg); | |||||
| }; | }; | ||||
| export const notifyActionSuccess = (actionMsg) => { | export const notifyActionSuccess = (actionMsg) => { | ||||
| toast.success(actionMsg??"Success", { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("success", actionMsg ?? "Success"); | |||||
| }; | }; | ||||
| export const notifyActionError = (actionMsg) => { | export const notifyActionError = (actionMsg) => { | ||||
| toast.error(`${actionMsg}`, { | |||||
| position: "bottom-right", | |||||
| autoClose: 20000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("error", `${actionMsg}`); | |||||
| }; | }; | ||||
| export const notifyActionWarning = (actionMsg) => { | export const notifyActionWarning = (actionMsg) => { | ||||
| toast.warn(`${actionMsg}`, { | |||||
| position: "bottom-right", | |||||
| autoClose: 5000, | |||||
| hideProgressBar: false, | |||||
| closeOnClick: true, | |||||
| pauseOnHover: true, | |||||
| draggable: true, | |||||
| progress: undefined, | |||||
| theme: "light", | |||||
| }) | |||||
| dispatchNotification("warning", `${actionMsg}`); | |||||
| } | } | ||||
| export function prettyJson(json) { | export function prettyJson(json) { | ||||