diff --git a/src/layout/MainLayout/Header/index.js b/src/layout/MainLayout/Header/index.js
index 5e52026..ca078fa 100644
--- a/src/layout/MainLayout/Header/index.js
+++ b/src/layout/MainLayout/Header/index.js
@@ -158,6 +158,9 @@ function Header(props) {
Gazette Issue
+
+ Audit Log
+
diff --git a/src/pages/AuditLog/AuditLogSearchForm.js b/src/pages/AuditLog/AuditLogSearchForm.js
new file mode 100644
index 0000000..9eff05e
--- /dev/null
+++ b/src/pages/AuditLog/AuditLogSearchForm.js
@@ -0,0 +1,180 @@
+// material-uisubDivision
+import {
+ Button,
+ // FormControlLabel,
+ Grid,
+ TextField,
+ Typography
+} from '@mui/material';
+import MainCard from "components/MainCard";
+import { useForm } from "react-hook-form";
+
+import {
+ // useEffect,
+ // useState
+} from "react";
+
+import * as React from "react";
+// import { useNavigate } from "react-router";
+import {PNSPS_BUTTON_THEME} from "themes/buttonConst";
+import {ThemeProvider} from "@emotion/react";
+import * as DateUtils from "utils/DateUtils";
+import * as UrlUtils from "utils/ApiPathConst";
+import * as HttpUtils from "utils/HttpUtils";
+import Loadable from 'components/Loadable';
+
+const LoadingComponent = Loadable(React.lazy(() => import('pages/extra-pages/LoadingComponent')));
+
+// ==============================|| DASHBOARD - DEFAULT ||============================== //
+
+
+const AuditLogSearchForm = ({ applySearch, searchCriteria}) => {
+ // const navigate = useNavigate();
+
+ const [minDate, setMinDate] = React.useState(searchCriteria.dateFrom);
+ const [maxDate, setMaxDate] = React.useState(searchCriteria.dateTo);
+ const [onDownload, setOnDownload] = React.useState(false);
+
+ const { reset, register, handleSubmit } = useForm()
+ const onSubmit = (data) => {
+
+ const temp = {
+ username: data.userName,
+ modifiedTo: data.modifiedTo,
+ modifiedFrom: data.modifiedFrom,
+ };
+ applySearch(temp);
+ };
+
+
+ function resetForm() {
+ reset();
+ }
+
+ function exportExcel() {
+ setOnDownload(true)
+ HttpUtils.fileDownload({
+ url: UrlUtils.AUDIT_LOG_EXPORT,
+ onResponse:()=>{
+ setOnDownload(false)
+ },
+ onError:()=>{
+ setOnDownload(false)
+ }
+ });
+ }
+
+ return (
+
+
+
+
+ );
+};
+
+export default AuditLogSearchForm;
diff --git a/src/pages/AuditLog/AuditLogTable.js b/src/pages/AuditLog/AuditLogTable.js
new file mode 100644
index 0000000..71c35a9
--- /dev/null
+++ b/src/pages/AuditLog/AuditLogTable.js
@@ -0,0 +1,81 @@
+// material-ui
+import * as React from 'react';
+import {FiDataGrid} from "components/FiDataGrid";
+import {useEffect} from "react";
+import * as DateUtils from "utils/DateUtils"
+import {useTheme} from "@emotion/react";
+import {
+ // Button,
+ // Typography,
+ useMediaQuery
+} from '@mui/material';
+// ==============================|| EVENT TABLE ||============================== //
+
+export default function AuditLogTable({recordList}) {
+ const [rows, setRows] = React.useState(recordList);
+
+ useEffect(() => {
+ setRows(recordList);
+ // console.log(recordList)
+ }, [recordList]);
+ const theme = useTheme();
+ const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
+
+ const columns = [
+ {
+ id: 'modified',
+ field: 'modified',
+ headerName: 'Date',
+ flex: isMdOrLg ? 1 : undefined,
+ valueGetter:(params)=>{
+ return DateUtils.datetimeStr(params?.value);
+ }
+ },
+ {
+ id: 'modifiedByName',
+ field: 'modifiedByName',
+ headerName: 'Username',
+ flex: isMdOrLg ? 1 : undefined,
+ },
+ {
+ id: 'tableName',
+ field: 'tableName',
+ headerName: 'Table Name',
+ flex: isMdOrLg ? 1 : undefined,
+ },
+ {
+ id: 'ref',
+ field: 'ref',
+ headerName: 'Ref',
+ flex: isMdOrLg ? 1 : undefined,
+ },
+ {
+ id: 'oldData',
+ field: 'oldData',
+ headerName: 'Old Data',
+ flex: isMdOrLg ? 2 : undefined,
+ },
+ {
+ id: 'newData',
+ field: 'newData',
+ headerName: 'New Data',
+ flex: isMdOrLg ? 2 : undefined,
+ },
+ ];
+
+ // function handleRowDoubleClick(params) {
+ // navigate('/user/'+ params.id);
+ // }
+
+ return (
+
+ 'auto'}
+ />
+
+ );
+}
diff --git a/src/pages/AuditLog/index.js b/src/pages/AuditLog/index.js
new file mode 100644
index 0000000..7ddda71
--- /dev/null
+++ b/src/pages/AuditLog/index.js
@@ -0,0 +1,118 @@
+// material-ui
+import {
+ Grid,
+ Typography,
+ Stack,
+ // Button
+} from '@mui/material';
+import MainCard from "components/MainCard";
+import { useEffect, useState } from "react";
+import axios from "axios";
+import { GET_AUDIT_LOG_LIST } from "utils/ApiPathConst";
+import * as React from "react";
+import * as DateUtils from "utils/DateUtils";
+
+import Loadable from 'components/Loadable';
+import { lazy } from 'react';
+const LoadingComponent = Loadable(lazy(() => import('../extra-pages/LoadingComponent')));
+const SearchForm = Loadable(lazy(() => import('./AuditLogSearchForm')));
+const EventTable = Loadable(lazy(() => import('./AuditLogTable')));
+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 AuditLogPage = () => {
+ const [record, setRecord] = useState([]);
+ const [searchCriteria, setSearchCriteria] = React.useState({
+ modifiedTo: DateUtils.dateStr(new Date()),
+ modifiedFrom: DateUtils.dateStr(new Date().setDate(new Date().getDate()-14)),
+ });
+ const [onReady, setOnReady] = useState(false);
+ const [changelocked, setChangeLocked] = React.useState(false);
+
+ React.useLayoutEffect(() => {
+ getUserList();
+ }, [changelocked]);
+
+ useEffect(() => {
+ if (record.length > 0) {
+ setOnReady(true);
+ }
+ }, [record]);
+
+ React.useLayoutEffect(() => {
+ getUserList();
+ }, [searchCriteria]);
+
+ function getUserList() {
+ axios.get(`${GET_AUDIT_LOG_LIST}`,
+ { params: searchCriteria }
+ )
+ .then((response) => {
+ if (response.status === 200) {
+ setRecord(response.data);
+ }
+ })
+ .catch(error => {
+ console.log(error);
+ return false;
+ });
+ }
+
+ function applySearch(input) {
+ setSearchCriteria(input);
+ }
+
+ return (
+ !onReady ?
+
+
+
+
+
+ :
+
+
+
+
+ Audit Log
+
+
+
+
+ {/*row 1*/}
+
+
+
+ {/*row 2*/}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AuditLogPage;
diff --git a/src/pages/User/DetailsPage_Individual/LoginGrid.js b/src/pages/User/DetailsPage_Individual/LoginGrid.js
new file mode 100644
index 0000000..9e367e8
--- /dev/null
+++ b/src/pages/User/DetailsPage_Individual/LoginGrid.js
@@ -0,0 +1,81 @@
+// material-ui
+import * as React from 'react';
+import {FiDataGrid} from "components/FiDataGrid";
+import {
+ // Button,
+ // Typography,
+ useMediaQuery
+} from '@mui/material';
+import * as DateUtils from "utils/DateUtils"
+// import {useNavigate} from "react-router-dom";
+// import {
+// isORGLoggedIn,
+// } from "utils/Utils";
+import {useTheme} from "@emotion/react";
+// import {getStatusIntl} from "utils/statusUtils/PublicNoteStatusUtils";
+// import {
+// FormattedMessage,
+// useIntl} from "react-intl";
+// ==============================|| EVENT TABLE ||============================== //
+
+export default function LoginGrid({rows}) {
+ // const navigate = useNavigate()
+ const theme = useTheme();
+ const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
+ // const intl = useIntl();
+
+ // const handleDetailClick = (params) => () => {
+ // navigate('/publicNotice/'+ params.id);
+ // };
+
+ // const remarkHeadername = rows.orgId===null?"我的備註":"Care Of / 我的備註"
+
+ const columns = [
+ {
+ id: 'loginTime',
+ field: 'loginTime',
+ headerName: "Date Time",
+ width: isMdOrLg ? 'auto' : 160,
+ flex: isMdOrLg ? 1 : undefined,
+ valueGetter:(params)=>{
+ return DateUtils.datetimeStr(params?.value);
+ }
+ },
+ {
+ id: 'result',
+ field: 'result',
+ headerName: 'Result',
+ width: isMdOrLg ? 'auto' : 300,
+ flex: isMdOrLg ? 2 : undefined,
+ valueGetter:(params)=>{
+ let resultMessage = "";
+ if (params.row.success>0){
+ resultMessage="Success"
+ }
+ if (params.row.success==0){
+ resultMessage="Fail"
+ }
+ if (params.row.success<0){
+ resultMessage= params.row.actionType + " by " + (params.row.modifiedBy == null?"multiple times failed login attempt.":params.row.modifiedBy)
+ }
+ return resultMessage;
+ }
+ },
+ ];
+
+ // function handleRowDoubleClick(params) {
+ // navigate('/publicNotice/'+ params.id);
+ // }
+
+ return (
+
+ 'auto'}
+ />
+
+ );
+}
diff --git a/src/pages/User/DetailsPage_Individual/index.js b/src/pages/User/DetailsPage_Individual/index.js
index b935105..45a3718 100644
--- a/src/pages/User/DetailsPage_Individual/index.js
+++ b/src/pages/User/DetailsPage_Individual/index.js
@@ -1,7 +1,8 @@
// material-ui
import * as React from "react";
-import { Grid, Typography, Button, Stack } from '@mui/material';
+import { Grid, Typography, Button, Stack, Box, Tab } from '@mui/material';
+import { TabPanel, TabContext, TabList } from '@mui/lab';
import FileList from "components/FileList"
import MainCard from "components/MainCard";
import * as HttpUtils from "utils/HttpUtils";
@@ -27,6 +28,7 @@ import * as ComboData from "utils/ComboData";
const LoadingComponent = Loadable(React.lazy(() => import('../../extra-pages/LoadingComponent')));
const UserInformationCard = Loadable(React.lazy(() => import('./UserInformationCard_Individual')));
const UserInformationPubCard = Loadable(React.lazy(() => import('./UserInformationCard_Individual_Pub')));
+const LoginGrid = Loadable(React.lazy(() => import('./LoginGrid')));
import {
isGLDLoggedIn,
@@ -41,7 +43,9 @@ const UserMaintainPage_Individual = () => {
const params = useParams();
const navigate = useNavigate();
const [formData, setFormData] = React.useState({})
+ const [loginLogData, setLoginLogData] = React.useState([])
const [isLoading, setLoding] = React.useState(true);
+ const [selectedTab, setSelectedTab] = React.useState("1");
React.useEffect(() => {
if (isORGLoggedIn()){
@@ -51,6 +55,10 @@ const UserMaintainPage_Individual = () => {
}
}, []);
+ const handleChange = (event, newValue) => {
+ setSelectedTab(newValue);
+ }
+
const loadData = () => {
setLoding(true);
if (isGLDLoggedIn()){
@@ -83,6 +91,7 @@ const UserMaintainPage_Individual = () => {
response.data["preferLocale"] = getObjectByType(ComboData.Locale, "type", response.data?.preferLocale);
setFormData(response.data);
+ getLoginLogList()
}
});
}
@@ -114,10 +123,23 @@ const UserMaintainPage_Individual = () => {
}
};
+ const getLoginLogList = () => {
+ HttpUtils.get({
+ url: `${UrlUtils.GET_LOGIN_LOG_LIST}`,
+ params:{
+ userId:params.id
+ },
+ onSuccess: function (response) {
+ // console.log(response)
+ setLoginLogData(response);
+ }
+ });
+ }
+
React.useEffect(() => {
setLoding(false);
- }, [formData]);
+ }, [loginLogData]);
return (
isLoading ?
@@ -173,15 +195,25 @@ const UserMaintainPage_Individual = () => {
{isGLDLoggedIn()?
-
- Files
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/User/DetailsPage_Organization/LoginGrid.js b/src/pages/User/DetailsPage_Organization/LoginGrid.js
new file mode 100644
index 0000000..9e367e8
--- /dev/null
+++ b/src/pages/User/DetailsPage_Organization/LoginGrid.js
@@ -0,0 +1,81 @@
+// material-ui
+import * as React from 'react';
+import {FiDataGrid} from "components/FiDataGrid";
+import {
+ // Button,
+ // Typography,
+ useMediaQuery
+} from '@mui/material';
+import * as DateUtils from "utils/DateUtils"
+// import {useNavigate} from "react-router-dom";
+// import {
+// isORGLoggedIn,
+// } from "utils/Utils";
+import {useTheme} from "@emotion/react";
+// import {getStatusIntl} from "utils/statusUtils/PublicNoteStatusUtils";
+// import {
+// FormattedMessage,
+// useIntl} from "react-intl";
+// ==============================|| EVENT TABLE ||============================== //
+
+export default function LoginGrid({rows}) {
+ // const navigate = useNavigate()
+ const theme = useTheme();
+ const isMdOrLg = useMediaQuery(theme.breakpoints.up('md'));
+ // const intl = useIntl();
+
+ // const handleDetailClick = (params) => () => {
+ // navigate('/publicNotice/'+ params.id);
+ // };
+
+ // const remarkHeadername = rows.orgId===null?"我的備註":"Care Of / 我的備註"
+
+ const columns = [
+ {
+ id: 'loginTime',
+ field: 'loginTime',
+ headerName: "Date Time",
+ width: isMdOrLg ? 'auto' : 160,
+ flex: isMdOrLg ? 1 : undefined,
+ valueGetter:(params)=>{
+ return DateUtils.datetimeStr(params?.value);
+ }
+ },
+ {
+ id: 'result',
+ field: 'result',
+ headerName: 'Result',
+ width: isMdOrLg ? 'auto' : 300,
+ flex: isMdOrLg ? 2 : undefined,
+ valueGetter:(params)=>{
+ let resultMessage = "";
+ if (params.row.success>0){
+ resultMessage="Success"
+ }
+ if (params.row.success==0){
+ resultMessage="Fail"
+ }
+ if (params.row.success<0){
+ resultMessage= params.row.actionType + " by " + (params.row.modifiedBy == null?"multiple times failed login attempt.":params.row.modifiedBy)
+ }
+ return resultMessage;
+ }
+ },
+ ];
+
+ // function handleRowDoubleClick(params) {
+ // navigate('/publicNotice/'+ params.id);
+ // }
+
+ return (
+
+ 'auto'}
+ />
+
+ );
+}
diff --git a/src/pages/User/DetailsPage_Organization/index.js b/src/pages/User/DetailsPage_Organization/index.js
index 3a795e0..ae3caf8 100644
--- a/src/pages/User/DetailsPage_Organization/index.js
+++ b/src/pages/User/DetailsPage_Organization/index.js
@@ -1,5 +1,7 @@
// material-ui
-import { Grid, Typography, Stack, Box, Button } from '@mui/material';
+import { Grid, Typography, Stack, Box, Button, Tab } from '@mui/material';
+import { TabPanel, TabContext, TabList } from '@mui/lab';
+
import { useEffect, useState } from "react";
import * as React from "react";
//import axios from "axios";
@@ -20,6 +22,7 @@ import titleBackgroundImg from 'assets/images/dashboard/gazette-bar.png'
import { useNavigate } from 'react-router-dom';
import * as ComboData from "utils/ComboData";
import {getObjectByValue,getObjectByType} from "utils/CommonFunction";
+const LoginGrid = Loadable(React.lazy(() => import('./LoginGrid')));
const BackgroundHead = {
backgroundImage: `url(${titleBackgroundImg})`,
@@ -45,32 +48,38 @@ const UserMaintainPage_Organization = () => {
const params = useParams();
const navigate = useNavigate();
const [userData, setUserData] = useState({})
+ const [loginLogData, setLoginLogData] = React.useState([])
const [orgData, setOrgData] = useState({})
const [isLoading, setLoding] = useState(true);
const intl = useIntl();
const { locale } = intl;
+ const [selectedTab, setSelectedTab] = React.useState("1");
const isPrimaryLocale = locale === 'en' ?"Yes":locale === 'zh-HK' ?"是":"是";
const notPrimaryLocale = locale === 'en' ?"No":locale === 'zh-HK' ?"否":"否";
- const _sx = {
- ml: 3,
- mb: 3,
- mt: 3,
- mr: 6,
- padding: "4 2 4 2",
- boxShadow: 1,
- border: 1,
- borderColor: '#DDD',
- '& .MuiDataGrid-cell': {
- borderTop: 1,
- borderBottom: 1,
- borderColor: "#EEE"
- },
- '& .MuiDataGrid-footerContainer': {
- border: 1,
- borderColor: "#EEE"
- }
+ // const _sx = {
+ // ml: 3,
+ // mb: 3,
+ // mt: 3,
+ // mr: 6,
+ // padding: "4 2 4 2",
+ // boxShadow: 1,
+ // border: 1,
+ // borderColor: '#DDD',
+ // '& .MuiDataGrid-cell': {
+ // borderTop: 1,
+ // borderBottom: 1,
+ // borderColor: "#EEE"
+ // },
+ // '& .MuiDataGrid-footerContainer': {
+ // border: 1,
+ // borderColor: "#EEE"
+ // }
+ // }
+
+ const handleChange = (event, newValue) => {
+ setSelectedTab(newValue);
}
useEffect(() => {
@@ -139,7 +148,7 @@ const UserMaintainPage_Organization = () => {
response.data["faxNumber"] = response.data.faxNo?.faxNumber;
response.data["fax_countryCode"] = response.data.faxNo?.countryCode;
- response.data["status"] = response.data?.locked?"locked":response.data?.status;
+ // response.data["status"] = response.data?.locked?"locked":response.data?.status;
response.data["preferLocale"] = getObjectByType(ComboData.Locale, "type", response.data?.preferLocale);
//response.data["orgId"] = response.data.brExpiryDate?DateUtils.dateStr(response.data.brExpiryDate):"";
@@ -148,6 +157,7 @@ const UserMaintainPage_Organization = () => {
// console.log(response.data)
setUserData(response.data);
setOrgData(response.orgList);
+ getLoginLogList()
}
});
}
@@ -175,10 +185,24 @@ const UserMaintainPage_Organization = () => {
}
};
+ const getLoginLogList = () => {
+ HttpUtils.get({
+ url: `${UrlUtils.GET_LOGIN_LOG_LIST}`,
+ params:{
+ userId:params.id
+ },
+ onSuccess: function (response) {
+ // console.log(response)
+ setLoginLogData(response);
+ }
+ });
+ }
+
+
useEffect(() => {
setLoding(false);
- }, [userData]);
+ }, [loginLogData]);
return (
isLoading ?
@@ -236,17 +260,27 @@ const UserMaintainPage_Organization = () => {
}
{isGLDLoggedIn()?
-
-
-
- Files
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
:null
diff --git a/src/routes/GLDUserRoutes.js b/src/routes/GLDUserRoutes.js
index efa9b76..b317f62 100644
--- a/src/routes/GLDUserRoutes.js
+++ b/src/routes/GLDUserRoutes.js
@@ -27,6 +27,7 @@ const EmailTemplateDetailPage = Loadable(lazy(() => import('pages/EmailTemplate/
const HolidayPage = Loadable(lazy(() => import('pages/Holiday')));
const GazetteIssuePage = Loadable(lazy(() => import('pages/GazetteIssue/index')));
const DrImport = Loadable(lazy(() => import('pages/Setting/DrImport')));
+const AuditLogPage = Loadable(lazy(() => import('pages/AuditLog/index')));
// ==============================|| MAIN ROUTING ||============================== //
@@ -125,6 +126,10 @@ const GLDUserRoutes = {
path: '/setting/drImport',
element:
},
+ {
+ path: '/setting/auditLog',
+ element:
+ },
]
},
]
diff --git a/src/utils/ApiPathConst.js b/src/utils/ApiPathConst.js
index 8b5311b..ad61c6e 100644
--- a/src/utils/ApiPathConst.js
+++ b/src/utils/ApiPathConst.js
@@ -69,6 +69,7 @@ export const GET_FILE_DELETE = apiPath+'/file/delete';
export const DR_EXPORT = apiPath+'/settings/dr/export';
export const DR_IMPORT = apiPath+'/settings/dr/import';
+export const AUDIT_LOG_EXPORT = apiPath+'/settings/auditLog-export';
@@ -105,6 +106,7 @@ export const POST_CHECK_APP_EXPRITY_DATE = apiPath+'/application/checkExprityDat
//GLD User
export const POST_ADMIN_USER_REGISTER = apiPath+'/user/registry';
export const DELETE_USER = apiPath+'/user';
+export const GET_LOGIN_LOG_LIST = apiPath+'/user/loginLogList';
export const GET_PUBLIC_NOTICE_APPLY_DETAIL = apiPath+'/application/application-detail';
export const SET_PUBLIC_NOTICE_GROUP_DETAIL = apiPath+'/application/application-group-detail';
export const SET_PUBLIC_NOTICE_STATUS_NOT_ACCEPT = apiPath+'/application/application-detail-status-not-accept';
@@ -115,6 +117,7 @@ export const SET_PUBLIC_NOTICE_STATUS_RESUBMIT = apiPath+'/application/applicati
export const SET_PUBLIC_NOTICE_STATUS_REVIEWED = apiPath+'/application/application-detail-status-reviewed';
export const SET_PUBLIC_NOTICE_STATUS_PUBLISH = apiPath+'/application/application-detail-status-publish';
export const UPDATE_PUBLIC_NOTICE_APPLY_DETAIL = apiPath+'/application/save';
+export const GET_AUDIT_LOG_LIST = apiPath+'/settings/auditLogList';
//gazette
export const GET_ISSUE_COMBO = apiPath+'/gazette-issue/combo';//GET