2 Revize

9 změnil soubory, kde provedl 454 přidání a 89 odebrání
  1. +96
    -62
      src/components/I18nProvider.js
  2. +6
    -3
      src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js
  3. +123
    -22
      src/pages/authentication/AuthWrapper.js
  4. +2
    -2
      src/pages/extra-pages/UserMenuPub/index.js
  5. +215
    -0
      src/pages/extra-pages/UserMenuPub1/index.js
  6. +9
    -0
      src/routes/LoginRoutes.js
  7. +1
    -0
      src/translations/en.json
  8. +1
    -0
      src/translations/zh-CN.json
  9. +1
    -0
      src/translations/zh-HK.json

+ 96
- 62
src/components/I18nProvider.js Zobrazit soubor

@@ -1,8 +1,8 @@
import { useState, useEffect, createContext } from 'react';
import { useState, useEffect, createContext, useMemo } from 'react';
import { IntlProvider } from 'react-intl';
import enMessages from '../translations/en.json';
import cnMessages from '../translations/zh-CN.json';
import hkMessages from '../translations/zh-HK.json';
import enBase from '../translations/en.json';
import cnBase from '../translations/zh-CN.json';
import hkBase from '../translations/zh-HK.json';

import { GET_COMBO, GET_CONTENT } from "utils/ApiPathConst";
import { get } from "utils/HttpUtils";
@@ -10,72 +10,106 @@ import { get } from "utils/HttpUtils";
const LocaleContext = createContext();

export const I18nProvider = ({ children }) => {
const systemMessages = {
"en": enMessages,
"zh": hkMessages,
"zh-HK": hkMessages,
"zh-CN": cnMessages
};
const [locale, setLocale] = useState('en');

// keep base messages immutable
const [systemMessages, setSystemMessages] = useState({
en: { ...enBase },
zh: { ...hkBase },
'zh-HK': { ...hkBase },
'zh-CN': { ...cnBase }
});

const [loaded, setLoaded] = useState(false);

useEffect(() => {
const saved = localStorage.getItem('locale');
if (!saved) localStorage.setItem('locale', 'en');
else setLocale(saved);
}, []);

const [locale, setLocale] = useState('en'); // Default locale, you can change this as per your requirement
const [messages, setMessages] = useState(systemMessages[locale]);
useEffect(() => {
let alive = true;

const loadTermsAndConditions = () => {
// load both endpoints then merge into state
const p1 = new Promise((resolve) => {
get({
url: GET_CONTENT,
onSuccess: (responseData) => {
for (const key in responseData) {
const value = responseData[key];
enMessages[key] = value.en??"";
cnMessages[key] = value.cn??"";
hkMessages[key] = value.zh??"";
}
}
url: GET_CONTENT,
onSuccess: (resp) => resolve(resp || {}),
onError: () => resolve({})
});
});

const p2 = new Promise((resolve) => {
get({
url: GET_COMBO,
onSuccess: (responseData) => {
for (let i = 0; i < responseData.length; i++) {
let item = responseData[i];
enMessages[item.key] = item.en;
cnMessages[item.key] = item.cn;
hkMessages[item.key] = item.zh;
}

}
url: GET_COMBO,
onSuccess: (resp) => resolve(resp || []),
onError: () => resolve([])
});
}

useEffect(() => {
loadTermsAndConditions();
if (localStorage.getItem('locale') === null) {
//no locale case
localStorage.setItem('locale', 'en');
}
else {
setLocale(localStorage.getItem('locale'));
}
}, []);

useEffect(() => {
// Load the messages for the selected locale
const fetchMessages = async () => {
setMessages(systemMessages[locale]);
};

fetchMessages();
}, [locale]);

return (
<LocaleContext.Provider value={{ locale, setLocale }} >
<IntlProvider locale={locale} messages={messages}>
{children}
</IntlProvider>
</LocaleContext.Provider>
);
}
});

Promise.all([p1, p2]).then(([contentMap, comboList]) => {
if (!alive) return;

setSystemMessages((prev) => {
// clone prev first (immutably)
const next = {
...prev,
en: { ...prev.en },
'zh-CN': { ...prev['zh-CN'] },
'zh-HK': { ...prev['zh-HK'] },
zh: { ...prev.zh }
};

// merge GET_CONTENT (object)
for (const key in contentMap) {
const v = contentMap[key] || {};
next.en[key] = v.en ?? "";
next['zh-CN'][key] = v.cn ?? "";
next['zh-HK'][key] = v.zh ?? "";
next.zh[key] = v.zh ?? "";
}

// merge GET_COMBO (array)
for (const item of comboList) {
if (!item?.key) continue;
next.en[item.key] = item.en ?? "";
next['zh-CN'][item.key] = item.cn ?? "";
next['zh-HK'][item.key] = item.zh ?? "";
next.zh[item.key] = item.zh ?? "";
}

return next;
});

setLoaded(true);
});
};

loadTermsAndConditions();

return () => {
alive = false;
};
}, []);

const messages = useMemo(() => {
return systemMessages[locale] || systemMessages.en;
}, [systemMessages, locale]);

return (
<LocaleContext.Provider value={{ locale, setLocale }}>
<IntlProvider
key={locale}
locale={locale}
messages={messages}
defaultLocale="en"
>
{loaded ? children : <div />}
</IntlProvider>
</LocaleContext.Provider>
);
};

export default LocaleContext;

+ 6
- 3
src/layout/MainLayout/Header/HeaderContent/LocaleSelector.js Zobrazit soubor

@@ -96,8 +96,9 @@ const LocaleSelector = () => {
<ListItem disablePadding>
<ListItemButton
onClick={() => {
setLocale("en")
setLocale("en");
localStorage.setItem('locale','en');
setOpen(false);
}}
>
<ListItemText
@@ -108,8 +109,9 @@ const LocaleSelector = () => {
<ListItem disablePadding>
<ListItemButton
onClick={() => {
setLocale("zh-HK")
setLocale("zh-HK");
localStorage.setItem('locale','zh-HK');
setOpen(false);
}}
>
<ListItemText
@@ -120,8 +122,9 @@ const LocaleSelector = () => {
<ListItem disablePadding>
<ListItemButton
onClick={() => {
setLocale("zh-CN")
setLocale("zh-CN");
localStorage.setItem('locale','zh-CN');
setOpen(false);
}}
>
<ListItemText


+ 123
- 22
src/pages/authentication/AuthWrapper.js Zobrazit soubor

@@ -1,13 +1,15 @@
import PropTypes from 'prop-types';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Box, Grid, Typography, Dialog, DialogContent, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import Loadable from 'components/Loadable';
import { lazy, useState } from 'react';
import { FormattedMessage, useIntl } from "react-intl";
import { lazy } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { checkSysEnv } from "utils/Utils";
import backbroundImg from 'assets/images/bg_ml.jpg';
import lgceImg from 'assets/images/2025_lgce.jpg'; // <-- your popup image
import 'assets/style/loginStyles.css';
import { SysContext } from 'components/SysSettingProvider';

const AuthCard = Loadable(lazy(() => import('./AuthCardCustom')));

@@ -18,23 +20,72 @@ const BackgroundHead = {
backgroundSize: 'cover'
};

const parseToDate = (v) => {
if (v == null || v === "") return null;

// timestamp: 10 digits (sec) or 13 digits (ms)
const s = String(v).trim();
if (/^\d{10,13}$/.test(s)) {
const n = Number(s);
return new Date(s.length === 10 ? n * 1000 : n);
}

// date-only: YYYY-MM-DD -> treat as LOCAL midnight
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
if (m) {
const y = Number(m[1]);
const mo = Number(m[2]) - 1; // 0-based
const d = Number(m[3]);
return new Date(y, mo, d, 0, 0, 0, 0);
}

// otherwise: ISO datetime or other formats
const d = new Date(s);
return Number.isNaN(d.getTime()) ? null : d;
};

const AuthWrapper = ({ children }) => {
const intl = useIntl();
const { sysSetting } = useContext(SysContext);

// --- Date control ---
// ===== Popup #1 (image popup) =====
const today = new Date();
const showUntil = new Date("2025-11-27T00:00:00"); // 8 Dec 2025 and onwards = hide popup
const [openPopup, setOpenPopup] = useState(today < showUntil);
const showUntil = new Date('2025-11-27T00:00:00');
const [openImagePopup, setOpenImagePopup] = useState(today < showUntil);

const handleCloseImagePopup = () => setOpenImagePopup(false);

// ===== Popup #2 (system popup) =====
const [openNoticePopup, setOpenNoticePopup] = useState(false);

const handleClosePopup = () => {
setOpenPopup(false);
const popupStart = useMemo(() => parseToDate(sysSetting?.loginPopupStart), [sysSetting?.loginPopupStart]);
const popupEnd = useMemo(() => parseToDate(sysSetting?.loginPopupEnd), [sysSetting?.loginPopupEnd]);

const popupHtml = useMemo(() => {
return intl.formatMessage({ id: 'loginPopupHtml', defaultMessage: '' }) || '';
}, [intl]);

useEffect(() => {
const now = new Date();

const inRange =
(popupStart ? now >= popupStart : true) &&
(popupEnd ? now <= popupEnd : true);

const hasMessage = popupHtml && popupHtml.trim().length > 0;

setOpenNoticePopup(Boolean(hasMessage && inRange));
}, [popupHtml, popupStart, popupEnd]);

const handleCloseNoticePopup = () => {
setOpenNoticePopup(false);
};

return (
<Box sx={{ minHeight: '87vh' }}>
<Dialog
open={openPopup}
onClose={handleClosePopup}
open={openImagePopup}
onClose={handleCloseImagePopup}
aria-labelledby="election-promo-title"
maxWidth="md"
PaperProps={{
@@ -48,7 +99,7 @@ const AuthWrapper = ({ children }) => {
<Box sx={{ position: 'relative' }}>
<IconButton
aria-label="Close"
onClick={handleClosePopup}
onClick={handleCloseImagePopup}
sx={{
position: 'absolute',
top: 6,
@@ -60,24 +111,74 @@ const AuthWrapper = ({ children }) => {
>
<CloseIcon />
</IconButton>

<DialogContent sx={{ p: 0 }}>
<Box
component="img"
src={lgceImg}
alt={intl.formatMessage({ id: 'lgce_alt', defaultMessage: '2025 Legislative Council General Election' })}
title={intl.formatMessage({ id: 'lgce_title', defaultMessage: '2025 Legislative Council General Election' })}
sx={{
display: 'block',
width: '100%',
maxWidth: '720px',
height: 'auto'
}}
sx={{ display: 'block', width: '100%', maxWidth: '720px', height: 'auto' }}
/>
</DialogContent>
</Box>
</Dialog>

{/* Page content */}
{/* =========================
Popup #2: system popup
========================= */}
<Dialog
open={openNoticePopup}
onClose={handleCloseNoticePopup}
maxWidth="md"
PaperProps={{
sx: {
borderRadius: 1,
overflow: 'hidden',
width: { xs: '95vw', md: 900 },
maxWidth: '95vw',
boxShadow: 10,
border: '1px solid #0c489e'
}
}}
>
<IconButton
aria-label="Close"
onClick={handleCloseNoticePopup}
sx={{
position: 'absolute',
top: 8,
right: 8,
zIndex: 10,
width: 34,
height: 34,
border: '1px solid rgba(0,0,0,0.5)',
borderRadius: 0,
bgcolor: '#fff'
}}
>
<CloseIcon />
</IconButton>

<DialogContent
sx={{
bgcolor: '#d9ecff',
p: 3,
maxHeight: { xs: '70vh', md: '60vh' },
overflowY: 'auto',
'& h1, & h2, & h3': { marginTop: 0, marginBottom: '12px' },
'& p': { marginBottom: '12px', lineHeight: 1.7 }
}}
>
<Typography component="div" sx={{ fontSize: 18, lineHeight: 1.8 }}>
<div dangerouslySetInnerHTML={{ __html: popupHtml }} />
</Typography>
</DialogContent>
</Dialog>

{/* =========================
Page content
========================= */}
<div style={BackgroundHead}>
<Grid
container
@@ -111,21 +212,21 @@ const AuthWrapper = ({ children }) => {
sx={{ minHeight: { md: 'calc(50vh)' } }}
>
<Grid item xs={12} sx={{ ml: 4, mt: 12, display: { xs: 'none', sm: 'block' } }}>
<Typography style={{ textAlign: "center", fontSize: "1.8rem" }}>
<Typography style={{ textAlign: 'center', fontSize: '1.8rem' }}>
<FormattedMessage id="HKSARGOV" />
</Typography>
<Typography style={{ textAlign: "center", fontSize: "1.8rem" }}>
<Typography style={{ textAlign: 'center', fontSize: '1.8rem' }}>
<FormattedMessage id="Gazette" />
</Typography>
<Typography style={{ textAlign: "center", fontSize: "1.8rem" }}>
<Typography style={{ textAlign: 'center', fontSize: '1.8rem' }}>
<FormattedMessage id="PNSPS_fullname" />
</Typography>
{checkSysEnv() !== '' ? (
<Typography style={{ color: 'red', textAlign: "center", fontSize: "1.8rem" }}>
<Typography style={{ color: 'red', textAlign: 'center', fontSize: '1.8rem' }}>
User Acceptance Test Environment
</Typography>
) : (
""
''
)}
</Grid>
</Grid>


+ 2
- 2
src/pages/extra-pages/UserMenuPub/index.js Zobrazit soubor

@@ -19,7 +19,7 @@ const LoadingComponent = Loadable(lazy(() => import('pages/extra-pages/LoadingCo

import DownloadIcon from '@mui/icons-material/Download';

const UserMenu = () => {
const UserMenuPub = () => {
const intl = useIntl();
const { locale } = intl;
const [onReady, setOnReady] = useState(false);
@@ -204,4 +204,4 @@ const UserMenu = () => {

}

export default UserMenu;
export default UserMenuPub;

+ 215
- 0
src/pages/extra-pages/UserMenuPub1/index.js Zobrazit soubor

@@ -0,0 +1,215 @@
import { Grid, Typography, Stack, } from '@mui/material';
import { useState, useEffect, lazy } from "react";

import Loadable from 'components/Loadable';
import { useIntl, FormattedMessage } from "react-intl";

import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
width: 'auto',
height: 'auto',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundColor: '#0C489E',
backgroundPosition: 'right'
}

const LoadingComponent = Loadable(lazy(() => import('pages/extra-pages/LoadingComponent')));

import DownloadIcon from '@mui/icons-material/Download';

const UserMenuPub1 = () => {
const intl = useIntl();
const { locale } = intl;
const [onReady, setOnReady] = useState(false);

useEffect(() => {
setOnReady(true);
}, [locale]);

const tableStyle = {
fontFamily: "arial, sans-serif",
borderCollapse: "collapse",
width: "100%",
}

const cellStyle = {
border: "1px solid #dddddd",
textAlign: "left",
padding: "8px"
};

const getRow = ({ title, orgEn, orgZh, orgCn, indEn, indZh, indCn }) => {

return <>
<tr>
<td style={cellStyle}>{title}</td>
<td style={cellStyle}><a href={locale=="zh-HK"?orgZh: locale=="en"?orgEn:orgCn} target='_brank'><DownloadIcon /></a></td>
<td style={cellStyle}><a href={locale=="zh-HK"?indZh: locale=="en"?indEn:indCn} target='_brank'><DownloadIcon /></a></td>
</tr>
</>
;
}

const pnspsurl = "https://"+window.location.hostname;


return (
!onReady ?
<Grid container sx={{ minHeight: '87vh', mb: 3 }} direction="column" justifyContent="center" alignItems="center">
<Grid item>
<LoadingComponent />
</Grid>
</Grid>
:
(
<Grid container sx={{ minHeight: '87vh', mb: 3}} direction="column" alignItems="center" >
<Grid item xs={12} md={12} width="100%">
<div style={BackgroundHead}>
<Stack direction="row" height='70px' justifyContent="flex-start" alignItems="center">
<Typography ml={15} color='#FFF' variant="h4" sx={{ display: { xs: 'none', sm: 'none', md: 'block'} }}>
<FormattedMessage id="userGuide" />
</Typography>
</Stack>
</div>
</Grid>
<Grid container justifyContent="center" alignItems="center" >
<Grid item xs={10} md={8} lg={6}>
<div style={{
textAlign: "justify",
textJustify: "interWord",
fontStyle: "normal"
}}>
<p>
<Typography
sx={{ mb: 2 }}
dangerouslySetInnerHTML={{
__html: intl.formatMessage({ id: "userGuideMessage" })
}}
/>
</p>
<p>
<table style={tableStyle}>
<tr>
<th style={cellStyle} width="70%"></th>
<th style={cellStyle} width="15%"><FormattedMessage id="forOrgUser" /></th>
<th style={cellStyle} width="15%"><FormattedMessage id="forIndUser" /></th>
</tr>

{getRow({
title: <FormattedMessage id="userGuide1" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/01 - Create account - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/01c - Create account - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/01sc - Create account - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/01 - Create account - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/01c - Create account - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/01sc - Create account - p 1.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide2" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/02 - Login - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/02c - Login - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/02sc - Login - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/02 - Login - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/02c - Login - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/02sc - Login - p 1.pdf"
})}

{/* UPDATED TO v2 */}
{getRow({
title: <FormattedMessage id="userGuide3" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/03 - Application for publishing a Public Notice in the Gazette - c 2.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/03c - Application for publishing a Public Notice in the Gazette - c 2.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/03sc - Application for publishing a Public Notice in the Gazette - c 2.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/03 - Application for publishing a Public Notice in the Gazette - p 2.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/03c - Application for publishing a Public Notice in the Gazette - p 2.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/03sc - Application for publishing a Public Notice in the Gazette - p 2.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide4" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/04 - Proofreading reply (with correction) - c 2.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/04c - Proofreading reply (with correction) - c 2.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/04sc - Proofreading reply (with correction) - c 2.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/04 - Proofreading reply (with correction) - p 2.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/04c - Proofreading reply (with correction) - p 2.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/04sc - Proofreading reply (with correction) - p 2.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide5" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/05 - Proofreading reply (pass for printing) - c 2.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/05c - Proofreading reply (pass for printing) - c 2.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/05sc - Proofreading reply (pass for printing) - c 2.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/05 - Proofreading reply (pass for printing) - p 2.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/05c - Proofreading reply (pass for printing) - p 2.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/05sc - Proofreading reply (pass for printing) - p 2.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide6" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/06 - Cancellation of application for publishing a Public Notice in the Gazette - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/06c - Cancellation of application for publishing a Public Notice in the Gazette - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/06sc - Cancellation of application for publishing a Public Notice in the Gazette - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/06 - Cancellation of application for publishing a Public Notice in the Gazette - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/06c - Cancellation of application for publishing a Public Notice in the Gazette - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/06sc - Cancellation of application for publishing a Public Notice in the Gazette - p 1.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide7" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/07 - Forgot password - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/07c - Forgot password - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/07sc - Forgot password - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/07 - Forgot password - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/07c - Forgot password - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/07sc - Forgot password - p 1.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide8" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/08 - Change password - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/08c - Change password - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/08sc - Change password - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/08 - Change password - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/08c - Change password - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/08sc - Change password - p 1.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuide9" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/09 - Language of email notification - c 1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/09c - Language of email notification - c 1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/09sc - Language of email notification - c 1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/09 - Language of email notification - p 1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/09c - Language of email notification - p 1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/09sc - Language of email notification - p 1.pdf"
})}

{getRow({
title: <FormattedMessage id="userGuidePub10" />,
orgEn: pnspsurl + "/user-guide-pub-1/eng/10-Payment-c1.pdf",
orgZh: pnspsurl + "/user-guide-pub-1/cht/10c-Payment-c1.pdf",
orgCn: pnspsurl + "/user-guide-pub-1/chs/10sc-Payment-c1.pdf",
indEn: pnspsurl + "/user-guide-pub-1/eng/10-Payment-p1.pdf",
indZh: pnspsurl + "/user-guide-pub-1/cht/10c-Payment-p1.pdf",
indCn: pnspsurl + "/user-guide-pub-1/chs/10sc-Payment-p1.pdf"
})}

</table>
<FormattedMessage id="userGuidePubNote" />
</p>
</div>
</Grid>
</Grid>
</Grid>
)
);

}

export default UserMenuPub1;

+ 9
- 0
src/routes/LoginRoutes.js Zobrazit soubor

@@ -17,6 +17,7 @@ const ImportantNoticePage = Loadable(lazy(() => import('pages/extra-pages/Import
const AboutUsPage = Loadable(lazy(() => import('pages/extra-pages/AboutUs')));
const UserMenuPage = Loadable(lazy(() => import('pages/extra-pages/UserMenu')));
const UserMenuPubPage = Loadable(lazy(() => import('pages/extra-pages/UserMenuPub')));
const UserMenuPub1Page = Loadable(lazy(() => import('pages/extra-pages/UserMenuPub1')));
const DatabaseHealthCheckPage = Loadable(lazy(() => import('pages/extra-pages/DatabaseHealthCheck')));


@@ -92,6 +93,14 @@ const LoginRoutes = {
path: 'userGuidePub',
element: <UserMenuPubPage/>
},
{
path: 'userGuidePub1',
element: <UserMenuPub1Page/>
},
{
path: 'userguide-fi',
element: <UserMenuPub1Page/>
},
{
path: 'databaseHealthCheck',
element: <DatabaseHealthCheckPage/>


+ 1
- 0
src/translations/en.json Zobrazit soubor

@@ -13,6 +13,7 @@
"PNSPS_fullname": "Public Notice Submission and Payment System",
"HKSARGOV": "The Government of the Hong Kong Special Administrative Region",
"homePageHeaderMessage": "<p>The <a target='_blank' href='https://pnsps.gld.gov.hk/'>Gazette Public Notice Submission and Payment System (PNSPS)</a> has been smoothly rolled out in August 2024 and will be fully implemented on 28 January 2026. Starting from <b>28 January 2026</b>, applications for publication of public notice in the Government Gazette and payment of gazette fee must be made online via the PNSPS. FPS (轉數快), PPS (繳費靈) and credit card are available in the PNSPS for payment of gazette fee. Applications made by other means will be returned to the applicant for re-submission via the PNSPS.</p><p>We would like to invite all applicants for publishing public notices in the Gazette to get well prepared for the change early, <a target='_blank' href='https://pnsps.gld.gov.hk/register'>register as PNSPS user</a> now and submit online applications to experience the benefits of the new online platform.</p><p>If you have any enquiries on the set-up of the above online payment methods, please contact your bank (for FPS and credit card) or the PPS (PPS hotline: 2311 9876).</p>",
"userGuideMessage": "The <a target='_blank' href='https://pnsps.gld.gov.hk/'>Gazette Public Notice Submission and Payment System (PNSPS))</a> will be fully implemented on 28 January 2026 and all applications and payment for publishing public notices on the Government Gazette must be made online via the PNSPS from 28 January 2026 onwards. FPS (轉數快), PPS (繳費靈) and credit card are available in the PNSPS for payment of gazette fee. Applications made by other means will be returned to the applicant for re-submission via the PNSPS. If you have any enquiries on the set-up of the above online payment methods, please contact your bank (for FPS and credit card) or the PPS (PPS hotline: 2311 9876).<br/><br/>Please download the new PNSPS User Guide effective from 28 January 2026 for your reference.",
"HKGLD": "Government Logistics Department",
"importantNotice": "Important Notice",
"privacyPolicy": "Privacy Policy",


+ 1
- 0
src/translations/zh-CN.json Zobrazit soubor

@@ -13,6 +13,7 @@
"PNSPS_fullname": "公共启事提交及缴费系统",
"HKSARGOV": "香港特别行政区政府",
"homePageHeaderMessage": "<p>「<a target='_blank' href='https://pnsps.gld.gov.hk/'>宪报公共启事提交及缴费系统</a>」已于2024年8月顺利启用,并将于2026年1月28日全面推行。由<b>2026年1月28日</b>开始,所有申请人必须使用网上「宪报公共启事提交及缴费系统」提交公共启事申请及缴交相关费用,申请人可以选择转数快(FPS)、 缴费灵(PPS)和信用卡缴交费用。以其他方式提交的申请将退回给申请人,重新透过系统提交。</p><p>现诚邀所有刊登宪报公共启事申请人及早为新安排做好准备,立即<a target='_blank' href='https://pnsps.gld.gov.hk/register'>登记成为系统用户</a>,使用系统提交申请,以体验新平台的便捷。</p><p>如果你对上述网上付款方式的设定有任何查询,请联络你的银行(转数快及信用卡)或缴费灵(缴费灵热线:2311 9876)。</p>",
"userGuideMessage": "「<a target='_blank' href='https://pnsps.gld.gov.hk/'>宪报公共启事提交及缴费系统</a>」(系统)将于2026年1月28日全面推行。由2026年1月28日开始,所有申请人必须使用系统提交公共启事申请及缴交相关费用。申请人可以选择转数快(FPS)、缴费灵(PPS)和信用卡缴交费用。以其他方式提交的申请将退回给申请人,重新透过系统提交。如果你对上述网上付款方式的设定有任何查询,请联络你的银行(转数快及信用卡)或缴费灵(缴费灵热线:2311 9876)。 <br/><br/>请你下载由2026年1月28日起生效的新系统使用者指南作参考。 ",
"HKGLD": "政府物流服务署",
"importantNotice": "重要告示",
"privacyPolicy": "私隐政策",


+ 1
- 0
src/translations/zh-HK.json Zobrazit soubor

@@ -13,6 +13,7 @@
"PNSPS_fullname": "公共啟事提交及繳費系統",
"HKSARGOV": "香港特別行政區政府",
"homePageHeaderMessage": "<p>「<a target='_blank' href='https://pnsps.gld.gov.hk/'>憲報公共啟事提交及繳費系統</a>」已於2024年8月順利啟用,並將於2026年1月28日全面推行。由<b>2026年1月28日</b>開始,所有申請人必須使用網上「憲報公共啟事提交及繳費系統」提交公共啟事申請及繳交相關費用,申請人可以選擇轉數快(FPS)、 繳費靈(PPS)和信用卡繳交費用。以其他方式提交的申請將退回給申請人,重新透過系統提交。</p><p>現誠邀所有刊登憲報公共啟事申請人及早為新安排做好準備,立即<a target='_blank' href='https://pnsps.gld.gov.hk/register'>登記成為系統用戶</a>,使用系統提交申請,以體驗新平台的便捷。</p><p>如果你對上述網上付款方式的設定有任何查詢,請聯絡你的銀行(轉數快及信用卡)或繳費靈(繳費靈熱線:2311 9876)。</p>",
"userGuideMessage": "「<a target='_blank' href='https://pnsps.gld.gov.hk/'>憲報公共啟事提交及繳費系統</a>」(系統)將於2026年1月28日全面推行。由2026年1月28日開始,所有申請人必須使用系統提交公共啟事申請及繳交相關費用。申請人可以選擇轉數快(FPS)、繳費靈(PPS)和信用卡繳交費用。以其他方式提交的申請將退回給申請人,重新透過系統提交。如果你對上述網上付款方式的設定有任何查詢,請聯絡你的銀行(轉數快及信用卡)或繳費靈(繳費靈熱線:2311 9876)。<br/><br/>請你下載由2026年1月28日起生效的新系統使用者指南作參考。",
"HKGLD": "政府物流服務署",
"importantNotice": "重要告示",
"privacyPolicy": "私隱政策",


Načítá se…
Zrušit
Uložit