diff --git a/package-lock.json b/package-lock.json index b4ce187..217ea7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "animate.css": "^4.1.1", "axios": "^0.27.2", "axios-hooks": "^4.0.0", + "date-fns": "^2.30.0", "formik": "^2.2.9", "gh-pages": "^4.0.0", "immer": "^9.0.21", @@ -38,7 +39,6 @@ "react-hook-form": "^7.45.1", "react-hot-toast": "^2.4.1", "react-infinite-scroll-component": "^6.1.0", - "react-moment": "^1.1.2", "react-redux": "^8.0.5", "react-router-dom": "^6.4.2", "react-tooltip": "^5.9.1", @@ -4707,6 +4707,21 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7488,16 +7503,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-moment": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-1.1.3.tgz", - "integrity": "sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA==", - "peerDependencies": { - "moment": "^2.29.0", - "prop-types": "^15.7.0", - "react": "^16.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-redux": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.7.tgz", @@ -11889,6 +11894,14 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -13888,12 +13901,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "react-moment": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-1.1.3.tgz", - "integrity": "sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA==", - "requires": {} - }, "react-redux": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.7.tgz", diff --git a/package.json b/package.json index d3442cf..1955197 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "animate.css": "^4.1.1", "axios": "^0.27.2", "axios-hooks": "^4.0.0", + "date-fns": "^2.30.0", "formik": "^2.2.9", "gh-pages": "^4.0.0", "immer": "^9.0.21", @@ -44,7 +45,6 @@ "react-hook-form": "^7.45.1", "react-hot-toast": "^2.4.1", "react-infinite-scroll-component": "^6.1.0", - "react-moment": "^1.1.2", "react-redux": "^8.0.5", "react-router-dom": "^6.4.2", "react-tooltip": "^5.9.1", diff --git a/src/history/History.tsx b/src/history/History.tsx index ca91c7e..18cb395 100644 --- a/src/history/History.tsx +++ b/src/history/History.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Divider, LinearProgress, Skeleton, Stack } from '@mui/material'; +import { Box, Button, DialogContent, Divider, LinearProgress, List, ListItem, ListItemText, Skeleton, Stack, Typography } from '@mui/material'; import RefreshIcon from '@mui/icons-material/Refresh'; import { useGetUserHistoryQuery } from '../store/user-history/user-history.api'; import ErrorPage from '../404/ErrorPage'; @@ -14,15 +14,57 @@ import { ellipsis } from '../shared/utils/css.utils'; import { UserHistory } from '../store/user-history/user-history.state'; import { GREY } from '../theme/palette'; import { startCase } from 'lodash'; +import { formatDistanceToNow, format } from 'date-fns'; +import { ReactNode, useState } from 'react'; +import Add from '@mui/icons-material/Add'; +import LowPriorityIcon from '@mui/icons-material/LowPriority'; +import Delete from '@mui/icons-material/Delete'; +import Edit from '@mui/icons-material/Edit'; +import produce from 'immer'; +import { shortenUserAgentHash } from '../shared/utils/user-agent'; +import DialogLayout from '../shared/components/dialog/DialogLayout'; + + function HistoryList() { - const { data, isError, isLoading, isFetching, refetch, error } = useGetUserHistoryQuery(); + const { data, sortedByDate, isError, isLoading, isFetching, refetch, error } = useGetUserHistoryQuery(undefined, { + selectFromResult: (data) => { + const copy = produce(data.data, draft => { + (draft ?? []).sort((a, b) => { + return (b.timestamp ?? 0) > (a.timestamp ?? 0) ? 1 : -1; + }); + }); + return { + ...data, + sortedByDate: copy + }; + } + }); + const [open, setOpen] = useState<{open: boolean; payload?: UserHistory}>({ + open: false + }); + + const handleClose = () => { + setOpen((current) => { + return { + ...current, + open: false + }; + }); + }; const handleRefresh = () => { refetch(); }; + const handleActionClick = (action: string, payload: UserHistory) => { + setOpen({ + open: true, + payload: payload + }); + }; + if (isLoading) { return ( @@ -37,10 +79,14 @@ function HistoryList() { return ; } - if (!data) { + if (!data || !sortedByDate) { return null; } + if (sortedByDate && sortedByDate.length < 1) { + return No History ; + } + return ( @@ -57,8 +103,13 @@ function HistoryList() { } + + + This is a list of all actions performed on Pokemons. ({ sortedByDate.length}) + + - +
{ @@ -79,7 +130,7 @@ function HistoryList() { { - data.map((history: UserHistory) => ( + sortedByDate.map((history: UserHistory) => ( - { transformTableData(history, col) } + { transformTableData(history, col, handleActionClick) } ); }) @@ -101,16 +152,74 @@ function HistoryList() {
+ + + + {open.payload?.user?.userHash} + + { open.payload?.user?.userAgent && + + { + Object.keys(open.payload.user.userAgent).map((key) => { + return ( + + + + ); + }) + } + + } + + + + ); } -function transformTableData(history: UserHistory, columnId: typeof TABLE_COLUMNS[number]) { +function transformTableData(history: UserHistory, columnId: typeof TABLE_COLUMNS[number], onClick: (action: string, payload: UserHistory)=>void) { + + const handleUserClick = () => { + onClick("user", history); + }; switch (columnId) { + case 'actionType': { + let icon: ReactNode = null; + switch(history.actionType) { + case 'add': { + icon = ; + break; + } + case 'remove': { + icon = ; + break; + } + case 'edit': { + icon = ; + break; + } + case 'reorder': { + icon = ; + break; + } + } + return + { icon } { startCase(history[columnId]) } + ; + } case 'user': { - return { history.user?.userHash } ; + return ; + } + case 'timestamp': { + return { formatDistanceToNow(history[columnId] ?? 0) } ago ; } default: { return
{ startCase(`${history[columnId]}`) }
; @@ -118,6 +227,7 @@ function transformTableData(history: UserHistory, columnId: typeof TABLE_COLUMNS } } -const TABLE_COLUMNS = ['actionEntity', 'actionEntitySource', 'actionEntitySourcePosition', 'actionEntityTarget', 'actionEntityTargetPosition', 'actionType', 'timestamp', 'user'] as const; + +const TABLE_COLUMNS = ['actionType', 'timestamp', 'actionEntity', 'actionEntitySource', 'actionEntitySourcePosition', 'actionEntityTarget', 'actionEntityTargetPosition', 'user'] as const; export default HistoryList; \ No newline at end of file diff --git a/src/routes/Layout.tsx b/src/routes/Layout.tsx index e853591..96c35dd 100644 --- a/src/routes/Layout.tsx +++ b/src/routes/Layout.tsx @@ -22,7 +22,7 @@ const Layout = () => { return ( - + theme.palette.mode === 'light' ? GREY[0] : null } }> diff --git a/src/shared/utils/table.utils.tsx b/src/shared/utils/table.utils.tsx index 9f4e422..a59e988 100644 --- a/src/shared/utils/table.utils.tsx +++ b/src/shared/utils/table.utils.tsx @@ -4,22 +4,20 @@ import { ellipsis } from "./css.utils"; export const StyledHeaderCell = styled(TableCell)(() => ({ ...ellipsis, - paddingTop: '10px', - paddingBottom: '10px', - fontSize: '15px', + paddingTop: '5px', + paddingBottom: '5px', + fontSize: '12px', borderRight: `1px solid ${GREY[400]}`, borderColor: GREY[400], backgroundColor: GREY[200], - maxWidth: '15rem', // the max width data cells can have })); export const StyledDataCell = styled(TableCell)(() => ({ ...ellipsis, - paddingTop: '10px', - paddingBottom: '10px', - fontSize: '14px', + paddingTop: '5px', + paddingBottom: '5px', + fontSize: '10px', //borderRight: `1px solid ${GREY[300]}`, borderBottom: "none", - maxWidth: '15rem', // the max width data cells can have })); diff --git a/src/shared/utils/user-agent.ts b/src/shared/utils/user-agent.ts index c3e33a1..35c64d5 100644 --- a/src/shared/utils/user-agent.ts +++ b/src/shared/utils/user-agent.ts @@ -1,5 +1,5 @@ export const shortenUserAgentHash = (hashString: string): string => { const first4 = hashString.substring(0, 4); const last4 = hashString.substring(hashString.length - 4); - return `${first4}...${last4}`; + return `${first4}${last4}`; }; \ No newline at end of file