From 712762ed365416e4aae5b85e8e1bb7a1bc2c03f1 Mon Sep 17 00:00:00 2001 From: Alex Cheung Date: Tue, 5 Mar 2024 12:55:16 +0800 Subject: [PATCH] update login log in user detail page --- src/pages/AuditLog/UserSearchForm.js | 198 ++++++++++++++++++ src/pages/AuditLog/UserTable.js | 142 +++++++++++++ src/pages/AuditLog/index.js | 110 ++++++++++ .../User/DetailsPage_Individual/LoginGrid.js | 18 +- .../User/DetailsPage_Individual/index.js | 20 +- .../DetailsPage_Organization/LoginGrid.js | 81 +++++++ .../User/DetailsPage_Organization/index.js | 96 ++++++--- src/routes/GLDUserRoutes.js | 5 + src/utils/ApiPathConst.js | 1 + 9 files changed, 633 insertions(+), 38 deletions(-) create mode 100644 src/pages/AuditLog/UserSearchForm.js create mode 100644 src/pages/AuditLog/UserTable.js create mode 100644 src/pages/AuditLog/index.js create mode 100644 src/pages/User/DetailsPage_Organization/LoginGrid.js diff --git a/src/pages/AuditLog/UserSearchForm.js b/src/pages/AuditLog/UserSearchForm.js new file mode 100644 index 0000000..c815e34 --- /dev/null +++ b/src/pages/AuditLog/UserSearchForm.js @@ -0,0 +1,198 @@ +// 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 Checkbox from "@mui/material/Checkbox"; +import * as React from "react"; +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; +import { useNavigate } from "react-router"; +import axios from "axios"; +import { GET_EMAIL_LIST } from 'utils/ApiPathConst'; +import {PNSPS_BUTTON_THEME} from "themes/buttonConst"; +import {ThemeProvider} from "@emotion/react"; + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + + +const UserSearchForm = ({ applySearch }) => { + const navigate = useNavigate(); + + const [type, setType] = useState([]); + const [locked, setLocked] = useState(false); + + + const { reset, register, handleSubmit } = useForm() + const onSubmit = (data) => { + + let typeArray = []; + + for (let i = 0; i < type.length; i++) { + typeArray.push(type[i].label); + } + + const temp = { + username: data.userName, + enName: data.fullenName, + post: data.post, + email: data.email, + locked: locked, + }; + applySearch(temp); + }; + + + function resetForm() { + setType([]); + setLocked(false); + reset(); + axios.get(`${GET_EMAIL_LIST}`) + .then(r => { + console.log(r) + }) + .catch(err => { + console.log(err) + }) + } + + const handleNewUserClick = () => { + // console.log(id) + navigate('/user/-1'); + // navigate('/user/' + id); + }; + + return ( + + +
+ + {/*row 1*/} + + + Search + + + {/*row 2*/} + + + + + + + + + + + + + + + + + + + + setLocked(event.target.checked)} + name="checked" + color="primary" + size="small" + /> + } + label={Locked} + /> + + + + {/*last row*/} + + + + + + + + + + + + + + + + + + + + +
+
+ ); +}; + +export default UserSearchForm; diff --git a/src/pages/AuditLog/UserTable.js b/src/pages/AuditLog/UserTable.js new file mode 100644 index 0000000..9f866bc --- /dev/null +++ b/src/pages/AuditLog/UserTable.js @@ -0,0 +1,142 @@ +// material-ui +import * as React from 'react'; +import { GridActionsCellItem,} from "@mui/x-data-grid"; +import {FiDataGrid} from "components/FiDataGrid"; +import EditIcon from '@mui/icons-material/Edit'; +import {useEffect} from "react"; +import {useNavigate} from "react-router-dom"; +import { useTheme } from '@mui/material/styles'; +import Checkbox from '@mui/material/Checkbox'; +import * as UrlUtils from "utils/ApiPathConst"; +import * as HttpUtils from 'utils/HttpUtils'; +import { notifyLockSuccess, notifyUnlockSuccess } from 'utils/CommonFunction'; + +// ==============================|| EVENT TABLE ||============================== // + +export default function UserTable({recordList,setChangeLocked}) { + const [rows, setRows] = React.useState(recordList); + const theme = useTheme(); + + const navigate = useNavigate() + + useEffect(() => { + setRows(recordList); + }, [recordList]); + + const handleEditClick = (id) => () => { + navigate('/user/'+ id); + }; + + const handleLock = (params) => () => { + setChangeLocked(false) + if (params.row.locked==true){ + doUnlock(params.id) + }else{ + doLock(params.id) + setRows(recordList); + } + } + + const doLock = (id) => { + HttpUtils.get({ + url: UrlUtils.GET_USER_LOCK+"/"+id, + onSuccess: function(){ + setRows(recordList); + setChangeLocked(true) + notifyLockSuccess() + } + }); + }; + + const doUnlock = (id) => { + HttpUtils.get({ + url: UrlUtils.GET_USER_UNLOCK+"/"+id, + onSuccess: function(){ + setRows(recordList); + setChangeLocked(true) + notifyUnlockSuccess() + } + }); + }; + + const columns = [ + { + field: 'actions', + type: 'actions', + headerName: 'Actions', + width: 100, + cellClassName: 'actions', + getActions: ({id}) => { + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="primary" + />] + }, + }, + { + id: 'username', + field: 'username', + headerName: 'User Name', + flex: 1, + }, + { + id: 'enName', + field: 'enName', + headerName: 'Full Name', + flex: 1, + }, + { + id: 'post', + field: 'post', + headerName: 'Post', + flex: 1, + }, + { + id: 'emailAddress', + field: 'emailAddress', + headerName: 'Email', + flex: 1, + }, + + { + id: 'locked', + field: 'locked', + type: 'bool', + headerName: 'Locked', + flex: 1, + renderCell: (params) => { + return ( + + ); + }, + }, + ]; + + 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..0e408b9 --- /dev/null +++ b/src/pages/AuditLog/index.js @@ -0,0 +1,110 @@ +// 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 { GLD_USER_PATH } from "utils/ApiPathConst"; +import * as React from "react"; + +import Loadable from 'components/Loadable'; +import { lazy } from 'react'; +const LoadingComponent = Loadable(lazy(() => import('../extra-pages/LoadingComponent'))); +const SearchForm = Loadable(lazy(() => import('./UserSearchForm'))); +const EventTable = Loadable(lazy(() => import('./UserTable'))); +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] = useState({}); + 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(`${GLD_USER_PATH}`, + { 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 ? + + + + + + : + + +
+ + View GLD User + +
+
+ + {/*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 index e806bf9..df785cc 100644 --- a/src/pages/User/DetailsPage_Individual/LoginGrid.js +++ b/src/pages/User/DetailsPage_Individual/LoginGrid.js @@ -46,10 +46,20 @@ export default function LoginGrid({rows}) { field: 'result', headerName: 'Result', width: isMdOrLg ? 'auto' : 300, - flex: isMdOrLg ? 1 : undefined, - // valueGetter:(params)=>{ - // return DateUtils.datetimeStr(params?.value); - // } + 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 + } + return resultMessage; + } }, ]; diff --git a/src/pages/User/DetailsPage_Individual/index.js b/src/pages/User/DetailsPage_Individual/index.js index e0e0b9f..70e7d04 100644 --- a/src/pages/User/DetailsPage_Individual/index.js +++ b/src/pages/User/DetailsPage_Individual/index.js @@ -43,6 +43,7 @@ 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"); @@ -90,6 +91,7 @@ const UserMaintainPage_Individual = () => { response.data["preferLocale"] = getObjectByType(ComboData.Locale, "type", response.data?.preferLocale); setFormData(response.data); + getLoginLogList() } }); } @@ -121,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 ? @@ -195,8 +210,7 @@ const UserMaintainPage_Individual = () => { diff --git a/src/pages/User/DetailsPage_Organization/LoginGrid.js b/src/pages/User/DetailsPage_Organization/LoginGrid.js new file mode 100644 index 0000000..df785cc --- /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 + } + 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 7e9bcc0..5a82336 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(() => { @@ -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..28bda55 100644 --- a/src/utils/ApiPathConst.js +++ b/src/utils/ApiPathConst.js @@ -105,6 +105,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';