diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js index 910da40..0e7157d 100644 --- a/src/layout/MainLayout/Header/index.js +++ b/src/layout/MainLayout/Header/index.js @@ -149,6 +149,9 @@ function Header(props) {
  • Holiday Setting
  • +
  • + Announcement +
  • diff --git a/src/pages/Announcement/Details/AnnouncementForm.js b/src/pages/Announcement/Details/AnnouncementForm.js new file mode 100644 index 0000000..0a78ed8 --- /dev/null +++ b/src/pages/Announcement/Details/AnnouncementForm.js @@ -0,0 +1,336 @@ +// material-ui +import { + Grid, + Typography, + Button, + TextField, + Stack, Box +} from '@mui/material'; +import { useFormik } from 'formik'; +import * as yup from 'yup'; +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as UrlUtils from "utils/ApiPathConst"; +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' +import ForwardIcon from '@mui/icons-material/Forward'; +import Divider from '@mui/material/Divider'; +import {useParams} from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { notifyActionSuccess } from 'utils/CommonFunction'; +import { useIntl } from "react-intl"; + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const AnnouncementForm = ({ loadedData }) => { + const intl = useIntl(); + const params = useParams(); + const navigate = useNavigate(); + + const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: 'auto', + height: 'auto', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' + } + + + const formik = useFormik({ + enableReinitialize: true, + initialValues: loadedData, + validationSchema: yup.object().shape({ + subjectEng: yup.string().max(255).required(), + subjectCht: yup.string().max(255).required(), + subjectChs: yup.string().max(255).required(), + contentEng: yup.string().required(), + contentCht: yup.string().required(), + contentChs: yup.string().required(), + }), + onSubmit: values => { + doSave(values); + } + }); + + const doSave = (values) => { + HttpUtils.post({ + url: UrlUtils.POST_ANNOUNCE_SAVE, + params: { + id: params?.id??0, + announceDate: values.announceDate, + subjectEng: values.subjectEng, + subjectCht: values.subjectCht, + subjectChs: values.subjectChs, + + contentEng: values.contentEng, + contentCht: values.contentCht, + contentChs: values.contentChs, + }, + onSuccess: function () { + notifyActionSuccess(intl.formatMessage({ id: 'submissionSuccess' }) + '!') + navigate("/setting/announcement"); + } + }); + } + + return ( + + +
    + + + Announcement + + +
    +
    + + + + + + +
    + + + + + + + + + + Announce Date: + + + + + + + + + English + + + + Subject: + + + + + + + + + + + Content: + + + + + + + + + Traditional Chinese + + + + Subject: + + + + + + + + + + + Content: + + + + + + + + + + + Simplified Chinese + + + + Subject: + + + + + + + + + + + + Content: + + + + + + + + +
    +
    +
    + +
    + ); +}; + + +export default AnnouncementForm; diff --git a/src/pages/Announcement/Details/index.js b/src/pages/Announcement/Details/index.js new file mode 100644 index 0000000..c24fa0b --- /dev/null +++ b/src/pages/Announcement/Details/index.js @@ -0,0 +1,64 @@ +// material-ui +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as UrlUtils from "utils/ApiPathConst"; +import * as DateUtils from "utils/DateUtils"; + +import { + Grid, +} from '@mui/material'; + +import { useParams } from 'react-router-dom'; +import Loadable from 'components/Loadable'; +import { lazy } from 'react'; +const LoadingComponent = Loadable(lazy(() => import('../../extra-pages/LoadingComponent'))); +const AnnouncementForm = Loadable(lazy(() => import('./AnnouncementForm'))); + + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const CreateForm = () => { + const params = useParams(); + + const [formData, setFormData] = React.useState({}); + const [isLoading, setLoding] = React.useState(true); + + React.useEffect(() => { + loadData(); + }, []); + + const loadData = () => { + setLoding(true); + if (params.id > 0) { + HttpUtils.get({ + url: `${UrlUtils.GET_ANNOUNCE}`+"/"+params.id, + onSuccess: function (responseData) { + responseData["announceDate"] = DateUtils.datetimeFieldFormat(responseData.announceDate); + setFormData(responseData); + } + }); + }else{ + setFormData({"announceDate": DateUtils.datetimeFieldFormat(new Date())}); + } + }; + + React.useEffect(() => { + if (formData !== null && formData != {}) setLoding(false); + }, [formData]); + + return ( + isLoading ? + + + + + + : + + ); +}; + + +export default CreateForm; diff --git a/src/pages/Announcement/Search/DataGrid.js b/src/pages/Announcement/Search/DataGrid.js new file mode 100644 index 0000000..bcbeab0 --- /dev/null +++ b/src/pages/Announcement/Search/DataGrid.js @@ -0,0 +1,83 @@ +// material-ui +import * as React from 'react'; +import { + Button, + Box +} from '@mui/material'; +import * as DateUtils from "utils/DateUtils"; +import { useNavigate } from "react-router-dom"; +import { FiDataGrid } from "components/FiDataGrid"; +// ==============================|| EVENT TABLE ||============================== // + +export default function SearchPublicNoticeTable({ recordList }) { + const [rows, setRows] = React.useState(recordList); + const navigate = useNavigate() + + React.useEffect(() => { + setRows(recordList); + }, [recordList]); + + const handleEditClick = (params) => () => { + navigate('/setting/announcement/details/' + params.id); + }; + + + const columns = [ + { + field: 'announceDate', + headerName: 'Announce Date', + width: 250, + cellClassName: 'announceDate', + renderCell: (params) => { + return ; + }, + }, + { + id: 'subjec', + field: 'subjec', + headerName: 'Subjec', + minWidth: 400, + renderCell: (params) => { + return <> + + {params.row.subjectEng} + {params.row.subjectCht} + {params.row.subjectChs} + + + }, + }, + { + id: 'content', + field: 'content', + headerName: 'Content', + flex:1, + minWidth: 400, + renderCell: (params) => { + return <> + + {params.row.contentEng} + {params.row.contentCht} + {params.row.contentChs} + + + } + }, + ]; + + function handleRowDoubleClick(params) { + navigate('/setting/announcement/details/' + params.id); + } + + return ( +
    + 'auto'} + onRowDoubleClick={handleRowDoubleClick} + /> +
    + ); +} diff --git a/src/pages/Announcement/Search/SearchForm.js b/src/pages/Announcement/Search/SearchForm.js new file mode 100644 index 0000000..500385c --- /dev/null +++ b/src/pages/Announcement/Search/SearchForm.js @@ -0,0 +1,155 @@ +// material-ui +import { + Button, + Grid, TextField, + Typography +} from '@mui/material'; +import MainCard from "components/MainCard"; +import { useForm } from "react-hook-form"; +import * as React from "react"; +import * as DateUtils from "utils/DateUtils"; +import {ThemeProvider} from "@emotion/react"; +import { useNavigate } from "react-router-dom"; +import {PNSPS_BUTTON_THEME} from "../../../themes/buttonConst"; +// ==============================|| DASHBOARD - DEFAULT ||============================== // + + +const SearchPublicNoticeForm = ({ applySearch, searchCriteria}) => { + const navigate = useNavigate() + + const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); + const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); + + const marginBottom = 2.5; + const { reset, register, handleSubmit } = useForm() + const onSubmit = (data) => { + const temp = { + key: data.key, + dateFrom: data.dateFrom, + dateTo: data.dateTo, + }; + applySearch(temp); + }; + + + function resetForm() { + reset(); + } + + + return ( + + +
    + + {/*row 1*/} + + + Search + + + {/*row 2*/} + + + + + + + + + { + setMinDate(DateUtils.dateStr(newValue)); + }} + InputLabelProps={{ + shrink: true + }} + /> + + + + To + + + + { + setMaxDate(DateUtils.dateStr(newValue)); + }} + id="dateTo" + type="date" + //label={"Submit Date(To)"} + defaultValue={searchCriteria.dateTo} + /> + + + + + + + {/*last row*/} + + + + + + + + + + + + + + + +
    +
    + ); +}; + +export default SearchPublicNoticeForm; diff --git a/src/pages/Announcement/Search/index.js b/src/pages/Announcement/Search/index.js new file mode 100644 index 0000000..073f486 --- /dev/null +++ b/src/pages/Announcement/Search/index.js @@ -0,0 +1,104 @@ +// material-ui +import { + Grid, + Typography, + Stack +} from '@mui/material'; +import MainCard from "components/MainCard"; +import * as UrlUtils from "utils/ApiPathConst"; +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as DateUtils from "utils/DateUtils"; + +import Loadable from 'components/Loadable'; +const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); +const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); +const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' + +const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: '100%', + height: '100%', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' +} + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const UserSearchPage_Individual = () => { + + const [record, setRecord] = React.useState([]); + const [searchCriteria, setSearchCriteria] = React.useState({ + dateTo: DateUtils.dateStr(new Date()), + dateFrom: DateUtils.dateStr(new Date().setDate(new Date().getDate() - 90)), + }); + const [onReady, setOnReady] = React.useState(false); + + React.useEffect(() => { + getDataList(); + }, []); + + React.useEffect(() => { + setOnReady(true); + }, [record]); + + React.useEffect(() => { + getDataList(); + }, [searchCriteria]); + + function getDataList() { + HttpUtils.get({ + url: UrlUtils.GET_ANNOUNCE_LIST, + params: searchCriteria, + onSuccess: function (responseData) { + setRecord(responseData); + } + }); + } + + function applySearch(input) { + setSearchCriteria(input); + } + + return ( + !onReady ? + + + + + + : + + +
    + + Announcement + +
    +
    + {/*row 1*/} + + + + {/*row 2*/} + + + + + +
    + ); +} +export default UserSearchPage_Individual; diff --git a/src/pages/Announcement/Search_Public/DataGrid.js b/src/pages/Announcement/Search_Public/DataGrid.js new file mode 100644 index 0000000..0482e52 --- /dev/null +++ b/src/pages/Announcement/Search_Public/DataGrid.js @@ -0,0 +1,62 @@ +// material-ui +import * as React from 'react'; +import * as DateUtils from "utils/DateUtils"; +import { FiDataGrid } from "components/FiDataGrid"; +import { FormattedMessage, useIntl } from "react-intl"; +// ==============================|| EVENT TABLE ||============================== // + +export default function SearchPublicNoticeTable({ recordList }) { + const [rows, setRows] = React.useState(recordList); + const intl = useIntl(); + const { locale } = intl; + + React.useEffect(() => { + setRows(recordList); + }, [recordList]); + + const columns = [ + { + field: 'announceDate', + headerName: , + width: 250, + cellClassName: 'announceDate', + renderCell: (params) => { + return DateUtils.datetimeStr(params?.value); + }, + }, + { + id: 'subject', + field: 'subject', + headerName: , + minWidth: 400, + renderCell: (params) => { + return <> + {locale === 'en' ?params.row.subjectEng:locale === 'zh-HK' ?params.row.subjectCht:params.row.subjectChs} + + }, + }, + { + id: 'content', + field: 'content', + headerName: , + flex:1, + minWidth: 400, + renderCell: (params) => { + return <> + {locale === 'en' ?params.row.contentEng:locale === 'zh-HK' ?params.row.contentCht:params.row.contentChs} + + } + }, + ]; + + return ( +
    + 'auto'} + /> +
    + ); +} diff --git a/src/pages/Announcement/Search_Public/SearchForm.js b/src/pages/Announcement/Search_Public/SearchForm.js new file mode 100644 index 0000000..f10ad95 --- /dev/null +++ b/src/pages/Announcement/Search_Public/SearchForm.js @@ -0,0 +1,158 @@ +// material-ui +import { + Button, + Grid, TextField, + Typography +} from '@mui/material'; +import MainCard from "components/MainCard"; +import { useForm } from "react-hook-form"; +import * as React from "react"; +import * as DateUtils from "utils/DateUtils"; +import { ThemeProvider } from "@emotion/react"; +import { useNavigate } from "react-router-dom"; +import { PNSPS_BUTTON_THEME } from "../../../themes/buttonConst"; +import { FormattedMessage, useIntl } from "react-intl"; +// ==============================|| DASHBOARD - DEFAULT ||============================== // + + +const SearchPublicNoticeForm = ({ applySearch, searchCriteria }) => { + const navigate = useNavigate() + + const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom); + const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo); + + const intl = useIntl(); + + const marginBottom = 2.5; + const { reset, register, handleSubmit } = useForm() + const onSubmit = (data) => { + const temp = { + key: data.key, + dateFrom: data.dateFrom, + dateTo: data.dateTo, + }; + applySearch(temp); + }; + + + function resetForm() { + reset(); + } + + + return ( + + +
    + + {/*row 1*/} + + + + + + {/*row 2*/} + + + + + + + + + { + setMinDate(DateUtils.dateStr(newValue)); + }} + InputLabelProps={{ + shrink: true + }} + /> + + + + + + + + { + setMaxDate(DateUtils.dateStr(newValue)); + }} + id="dateTo" + type="date" + //label={"Submit Date(To)"} + defaultValue={searchCriteria.dateTo} + /> + + + + + + + {/*last row*/} + + + + + + + + + + + + + + + +
    +
    + ); +}; + +export default SearchPublicNoticeForm; diff --git a/src/pages/Announcement/Search_Public/index.js b/src/pages/Announcement/Search_Public/index.js new file mode 100644 index 0000000..351edb5 --- /dev/null +++ b/src/pages/Announcement/Search_Public/index.js @@ -0,0 +1,104 @@ +// material-ui +import { + Grid, + Typography, + Stack +} from '@mui/material'; +import MainCard from "components/MainCard"; +import * as UrlUtils from "utils/ApiPathConst"; +import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as DateUtils from "utils/DateUtils"; + +import Loadable from 'components/Loadable'; +const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent'))); +const SearchForm = Loadable(React.lazy(() => import('./SearchForm'))); +const EventTable = Loadable(React.lazy(() => import('./DataGrid'))); +import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' +import { FormattedMessage } from "react-intl"; + +const BackgroundHead = { + backgroundImage: `url(${titleBackgroundImg})`, + width: '100%', + height: '100%', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: '#0C489E', + backgroundPosition: 'right' +} + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +const UserSearchPage_Individual = () => { + const [record, setRecord] = React.useState([]); + const [searchCriteria, setSearchCriteria] = React.useState({ + dateTo: DateUtils.dateStr(new Date()), + dateFrom: DateUtils.dateStr(new Date().setDate(new Date().getDate() - 90)), + }); + const [onReady, setOnReady] = React.useState(false); + + React.useEffect(() => { + getDataList(); + }, []); + + React.useEffect(() => { + setOnReady(true); + }, [record]); + + React.useEffect(() => { + getDataList(); + }, [searchCriteria]); + + function getDataList() { + HttpUtils.get({ + url: UrlUtils.GET_ANNOUNCE_LIST, + params: searchCriteria, + onSuccess: function (responseData) { + setRecord(responseData); + } + }); + } + + function applySearch(input) { + setSearchCriteria(input); + } + + return ( + !onReady ? + + + + + + : + + +
    + + + +
    +
    + {/*row 1*/} + + + + {/*row 2*/} + + + + + +
    + ); +} +export default UserSearchPage_Individual; diff --git a/src/pages/dashboard/Public/Message.js b/src/pages/dashboard/Public/Message.js index f959904..8d13377 100644 --- a/src/pages/dashboard/Public/Message.js +++ b/src/pages/dashboard/Public/Message.js @@ -26,7 +26,7 @@ const SearchDemandNoteForm = () => { const loadData = () => { HttpUtils.get({ - url: UrlUtils.GET_MSG_DESHBOARD, + url: UrlUtils.GET_MSG_DASHBOARD, onSuccess: function (response) { let list = [] response.map((item) => { diff --git a/src/pages/dashboard/Public/Notice.js b/src/pages/dashboard/Public/Notice.js index b7a1029..e1ce7b8 100644 --- a/src/pages/dashboard/Public/Notice.js +++ b/src/pages/dashboard/Public/Notice.js @@ -1,29 +1,67 @@ // material-ui import { + Stack, Typography, - Stack + Divider } from '@mui/material'; import MainCard from "components/MainCard"; import * as React from "react"; +import * as HttpUtils from "utils/HttpUtils"; +import * as UrlUtils from "utils/ApiPathConst"; +import * as DateUtils from "utils/DateUtils"; +import { useIntl} from "react-intl"; // ==============================|| DASHBOARD - DEFAULT ||============================== // const SearchDemandNoteForm = () => { + const [itemList, setItemList] = React.useState([]); + const [listData, setListData] = React.useState([]); + const intl = useIntl(); + const { locale } = intl; + + + React.useEffect(() => { + loadData(); + }, []); + + React.useEffect(() => { + let list = [] + if(listData == []) return; + listData.map((item) => { + list.push( + + {locale === 'en' ?item.subjectEng:locale === 'zh-HK' ?item.subjectCht:item.subjectChs} + {DateUtils.datetimeStr(item.announceDate)} + {locale === 'en' ?item.contentEng:locale === 'zh-HK' ?item.contentCht:item.contentChs} + + + ) + }); + setItemList(list); + }, [listData,intl]); + + const loadData = () => { + + HttpUtils.get({ + url: UrlUtils.GET_ANNOUNCE_DASHBOARD, + onSuccess: function (response) { + setListData(response); + } + }); + }; + + return ( - - - - + + {itemList} - - ); }; diff --git a/src/pages/dashboard/Public/index.js b/src/pages/dashboard/Public/index.js index d6b2a5b..ee7f1df 100644 --- a/src/pages/dashboard/Public/index.js +++ b/src/pages/dashboard/Public/index.js @@ -10,7 +10,7 @@ import { } from '@mui/material'; import { isORGLoggedIn, } from "utils/Utils"; import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png' -import {FormattedMessage, useIntl} from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import AdsClickRoundedIcon from '@mui/icons-material/AdsClickRounded'; import * as React from "react"; import Loadable from 'components/Loadable'; @@ -51,10 +51,10 @@ const DashboardDefault = () => { + + { + + import('pages/DemandNote/Details' const GFMIS_Search = Loadable(lazy(() => import('pages/GFMIS'))); const UserMaintainPage = Loadable(lazy(() => import('pages/User/GLDUserProfile'))); const SystemSetting = Loadable(lazy(() => import('pages/Setting/SystemSetting'))); +const AnnouncementDetails = Loadable(lazy(() => import('pages/Announcement/Details'))); +const AnnouncementSearch = Loadable(lazy(() => import('pages/Announcement/Search'))); // ==============================|| MAIN ROUTING ||============================== // @@ -90,6 +92,14 @@ const GLDUserRoutes = { path: '/setting/sys', element: }, + { + path: '/setting/announcement', + element: + }, + { + path: '/setting/announcement/details/:id', + element: + }, ] }, ] diff --git a/src/routes/PublicUserRoutes.js b/src/routes/PublicUserRoutes.js index 8ec62fc..c707e3a 100644 --- a/src/routes/PublicUserRoutes.js +++ b/src/routes/PublicUserRoutes.js @@ -28,6 +28,8 @@ const UserMaintainPage_Organization = Loadable(lazy(() => import('pages/User/Det const OrganizationDetailPage = Loadable(lazy(() => import('pages/Organization/DetailPage'))); const Msg_Details = Loadable(lazy(() => import('pages/Message/Details'))); const Msg_Search = Loadable(lazy(() => import('pages/Message/Search'))); +const AnnouncementSearch = Loadable(lazy(() => import('pages/Announcement/Search_Public'))); + // ==============================|| MAIN ROUTING ||============================== // @@ -130,6 +132,10 @@ const PublicDashboard = { path: '/msg/search', element: }, + { + path: '/announcement/search', + element: + }, ] }, ] diff --git a/src/themes/buttonConst.js b/src/themes/buttonConst.js index 6ac414b..025c764 100644 --- a/src/themes/buttonConst.js +++ b/src/themes/buttonConst.js @@ -47,6 +47,10 @@ export const PNSPS_BUTTON_THEME = createTheme({ main: '#448DF2', contrastText: '#FFFFFF', }, + green:{ + main: '#4ac234', + contrastText: '#FFFFFF', + }, orange: { main: '#ed9740', light: '#ff5e5e', diff --git a/src/translations/en.json b/src/translations/en.json index 689a55a..f4c6a1e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -401,6 +401,9 @@ "create": "Create", "confirmTo": "Confirm to ", + "content": "Content", + "subject": "Subject", + "Dashboard": "Dashboard", "event": "Event" } \ No newline at end of file diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json index b0eeb96..2c6e178 100644 --- a/src/translations/zh-CN.json +++ b/src/translations/zh-CN.json @@ -397,6 +397,9 @@ "create": "创建", "confirmTo": "确定", + "content": "内容", + "subject": "主题", + "Dashboard": "仪表板", "event": "活动" } \ No newline at end of file diff --git a/src/translations/zh-HK.json b/src/translations/zh-HK.json index 2b2ee40..33bc175 100644 --- a/src/translations/zh-HK.json +++ b/src/translations/zh-HK.json @@ -402,6 +402,9 @@ "create": "創建", "confirmTo": "確定", + "content": "内容", + "subject": "主題", + "Dashboard": "儀表板", "event": "活動" } \ No newline at end of file diff --git a/src/utils/ApiPathConst.js b/src/utils/ApiPathConst.js index 644f16d..040b280 100644 --- a/src/utils/ApiPathConst.js +++ b/src/utils/ApiPathConst.js @@ -49,7 +49,12 @@ export const GET_PUB_ORG_MARK_AS_NON_CREDITOR = apiPath+'/org/pub/mark-as-non-cr export const GET_MSG_DETAILS = apiPath+'/msg/details'; export const GET_MSG_LIST = apiPath+'/msg/list'; -export const GET_MSG_DESHBOARD = apiPath+'/msg/list/deshboard'; +export const GET_MSG_DASHBOARD = apiPath+'/msg/list/deshboard'; + +export const POST_ANNOUNCE_SAVE = apiPath+'/announcement/save'; +export const GET_ANNOUNCE = apiPath+'/announcement'; +export const GET_ANNOUNCE_LIST = apiPath+'/announcement/list'; +export const GET_ANNOUNCE_DASHBOARD = apiPath+'/announcement/dashboard'; export const CHECK_OVERDUE = apiPath+'/application/check-overdue';