From 6f7f5c069fcf26b4e8e012f539c4e06ba18101d3 Mon Sep 17 00:00:00 2001 From: Thegan Govender Date: Thu, 27 Apr 2023 18:29:26 +0200 Subject: [PATCH] Added Activity view for users/items/library Backend and frontend changes to have an activity view per user/ item or library Switched over to MUI for the tables as the library provides alot of solutions such as pagination or sorting built in Reworked info components to allow for Activity-Table component to be reusable and remove redundant components --- backend/api.js | 92 +++++ backend/stats.js | 2 + package-lock.json | 166 ++++++--- package.json | 3 +- src/App.css | 1 + src/App.js | 2 +- .../components/activity/activity-table.js | 313 +++++++++-------- .../components/general/last-watched-card.js | 2 +- src/pages/components/general/navbar.js | 2 +- src/pages/components/item-info.js | 114 +++++- .../components/item-info/item-activity.js | 67 ++++ .../components/item-info/item-details.js | 103 ------ .../item-info/more-items/more-items-card.js | 2 +- src/pages/components/library-info.js | 87 ++++- .../RecentlyAdded/recently-added-card.js | 2 +- ...library-details.js => library-activity.js} | 35 +- src/pages/components/settings/backupfiles.js | 332 +++++++++++------- src/pages/components/user-info.js | 103 +++++- .../components/user-info/user-activity.js | 65 ++++ .../components/user-info/user-details.js | 87 ----- src/pages/css/activity/activity-table.css | 154 ++++---- src/pages/css/items/item-details.css | 9 + src/pages/css/lastplayed.css | 18 +- src/pages/css/loading.css | 22 +- src/pages/css/sessions.css | 12 +- src/pages/css/settings/backups.css | 2 +- src/pages/css/statCard.css | 11 +- src/pages/css/users/user-details.css | 2 +- src/pages/css/users/users.css | 175 --------- src/pages/login.js | 7 +- src/pages/signup.js | 6 + src/pages/users.js | 224 ++++++------ 32 files changed, 1285 insertions(+), 937 deletions(-) create mode 100644 src/pages/components/item-info/item-activity.js delete mode 100644 src/pages/components/item-info/item-details.js rename src/pages/components/library/{library-details.js => library-activity.js} (52%) create mode 100644 src/pages/components/user-info/user-activity.js delete mode 100644 src/pages/components/user-info/user-details.js diff --git a/backend/api.js b/backend/api.js index 13b4cae..c417bea 100644 --- a/backend/api.js +++ b/backend/api.js @@ -199,6 +199,7 @@ router.get("/getHistory", async (req, res) => { ...row, results: [] }; + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); } }); @@ -212,6 +213,97 @@ router.get("/getHistory", async (req, res) => { }); +router.post("/getLibraryHistory", async (req, res) => { + try { + const { libraryid } = req.body; + const { rows } = await db.query( + `select a.* from jf_playback_activity a join jf_library_items i on i."Id"=a."NowPlayingItemId" where i."ParentId"='${libraryid}' order by "ActivityDateInserted" desc` + ); + const groupedResults = {}; + rows.forEach(row => { + if (groupedResults[row.NowPlayingItemId+row.EpisodeId]) { + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } else { + groupedResults[row.NowPlayingItemId+row.EpisodeId] = { + ...row, + results: [] + }; + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } + }); + + res.send(Object.values(groupedResults)); + } catch (error) { + console.log(error); + res.send(error); + } +}); + + +router.post("/getItemHistory", async (req, res) => { + try { + const { itemid } = req.body; + + const { rows } = await db.query( + `select jf_playback_activity.* + from jf_playback_activity jf_playback_activity + where + ("EpisodeId"='${itemid}' OR "SeasonId"='${itemid}' OR "NowPlayingItemId"='${itemid}');` + ); + + const groupedResults = {}; + rows.forEach(row => { + if (groupedResults[row.NowPlayingItemId+row.EpisodeId]) { + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } else { + groupedResults[row.NowPlayingItemId+row.EpisodeId] = { + ...row, + results: [] + }; + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } + }); + + res.send(Object.values(groupedResults)); + } catch (error) { + console.log(error); + res.send(error); + } +}); + +router.post("/getUserHistory", async (req, res) => { + try { + const { userid } = req.body; + + const { rows } = await db.query( + `select jf_playback_activity.* + from jf_playback_activity jf_playback_activity + where "UserId"='${userid}';` + ); + + const groupedResults = {}; + rows.forEach(row => { + if (groupedResults[row.NowPlayingItemId+row.EpisodeId]) { + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } else { + groupedResults[row.NowPlayingItemId+row.EpisodeId] = { + ...row, + results: [] + }; + groupedResults[row.NowPlayingItemId+row.EpisodeId].results.push(row); + } + }); + + res.send(Object.values(groupedResults)); + } catch (error) { + console.log(error); + res.send(error); + } +}); + + + + router.get("/getAdminUsers", async (req, res) => { try { const { rows:config } = await db.query('SELECT * FROM app_config where "ID"=1'); diff --git a/backend/stats.js b/backend/stats.js index bd47456..d9740b5 100644 --- a/backend/stats.js +++ b/backend/stats.js @@ -284,6 +284,8 @@ router.post("/getLibraryLastPlayed", async (req, res) => { }); + + router.post("/getViewsOverTime", async (req, res) => { try { const { days } = req.body; diff --git a/package-lock.json b/package-lock.json index 4f7b5ff..7db8263 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@mui/material": "^5.11.10", + "@mui/material": "^5.12.2", + "@mui/x-data-grid": "^6.2.1", "@nivo/api": "^0.74.1", "@nivo/bar": "^0.80.0", "@nivo/core": "^0.80.0", @@ -2121,8 +2122,9 @@ } }, "node_modules/@emotion/cache": { - "version": "11.10.5", - "license": "MIT", + "version": "11.10.7", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.7.tgz", + "integrity": "sha512-VLl1/2D6LOjH57Y8Vem1RoZ9haWF4jesHDGiHtKozDQuBIkJm2gimVo0I02sWCuzZtVACeixTVB4jeE8qvCBoQ==", "dependencies": { "@emotion/memoize": "^0.8.0", "@emotion/sheet": "^1.2.1", @@ -2148,7 +2150,8 @@ }, "node_modules/@emotion/react": { "version": "11.10.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz", + "integrity": "sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.10.6", @@ -2185,7 +2188,8 @@ }, "node_modules/@emotion/styled": { "version": "11.10.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz", + "integrity": "sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.10.6", @@ -2998,14 +3002,15 @@ "license": "MIT" }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.118", - "license": "MIT", + "version": "5.0.0-alpha.127", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.127.tgz", + "integrity": "sha512-FoRQd0IOH9MnfyL5yXssyQRnC4vXI+1bwkU1idr+wNkP1ZfxE+JsThHcfl1dy5azLssVUGTtQFD9edQLdbyJog==", "dependencies": { - "@babel/runtime": "^7.20.13", + "@babel/runtime": "^7.21.0", "@emotion/is-prop-valid": "^1.2.0", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.9", - "@popperjs/core": "^2.11.6", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.12.0", + "@popperjs/core": "^2.11.7", "clsx": "^1.2.1", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -3030,29 +3035,32 @@ }, "node_modules/@mui/base/node_modules/react-is": { "version": "18.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.11.9", - "license": "MIT", + "version": "5.12.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.2.tgz", + "integrity": "sha512-Qn7dy8tql6T0hY6gTFPkpWlnqVVFGu5Z6QzEzUSzzmLZpfAx4kf8sFz0PHiB7gU5yrqcZF9picMx1shpRY/rXw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" } }, "node_modules/@mui/material": { - "version": "5.11.10", - "license": "MIT", + "version": "5.12.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.12.2.tgz", + "integrity": "sha512-XOVy6fVC0rI2dEwDq/1s4Te2hewTUe6lznzeVnruyATGkdmM06WnHqkZOoLVIWo9hWwAxpcgTDcAIVpFtt1nrw==", "dependencies": { - "@babel/runtime": "^7.20.13", - "@mui/base": "5.0.0-alpha.118", - "@mui/core-downloads-tracker": "^5.11.9", - "@mui/system": "^5.11.9", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.9", + "@babel/runtime": "^7.21.0", + "@mui/base": "5.0.0-alpha.127", + "@mui/core-downloads-tracker": "^5.12.2", + "@mui/system": "^5.12.1", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.12.0", "@types/react-transition-group": "^4.4.5", "clsx": "^1.2.1", - "csstype": "^3.1.1", + "csstype": "^3.1.2", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -3088,11 +3096,12 @@ "license": "MIT" }, "node_modules/@mui/private-theming": { - "version": "5.11.9", - "license": "MIT", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.12.0.tgz", + "integrity": "sha512-w5dwMen1CUm1puAtubqxY9BIzrBxbOThsg2iWMvRJmWyJAPdf3Z583fPXpqeA2lhTW79uH2jajk5Ka4FuGlTPg==", "dependencies": { - "@babel/runtime": "^7.20.13", - "@mui/utils": "^5.11.9", + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.12.0", "prop-types": "^15.8.1" }, "engines": { @@ -3113,12 +3122,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.11.9", - "license": "MIT", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.12.0.tgz", + "integrity": "sha512-frh8L7CRnvD0RDmIqEv6jFeKQUIXqW90BaZ6OrxJ2j4kIsiVLu29Gss4SbBvvrWwwatR72sBmC3w1aG4fjp9mQ==", "dependencies": { - "@babel/runtime": "^7.20.13", - "@emotion/cache": "^11.10.5", - "csstype": "^3.1.1", + "@babel/runtime": "^7.21.0", + "@emotion/cache": "^11.10.7", + "csstype": "^3.1.2", "prop-types": "^15.8.1" }, "engines": { @@ -3143,16 +3153,17 @@ } }, "node_modules/@mui/system": { - "version": "5.11.9", - "license": "MIT", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.12.1.tgz", + "integrity": "sha512-Po+sicdV3bbRYXdU29XZaHPZrW7HUYUqU1qCu77GCCEMbahC756YpeyefdIYuPMUg0OdO3gKIUfDISBrkjJL+w==", "dependencies": { - "@babel/runtime": "^7.20.13", - "@mui/private-theming": "^5.11.9", - "@mui/styled-engine": "^5.11.9", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.9", + "@babel/runtime": "^7.21.0", + "@mui/private-theming": "^5.12.0", + "@mui/styled-engine": "^5.12.0", + "@mui/types": "^7.2.4", + "@mui/utils": "^5.12.0", "clsx": "^1.2.1", - "csstype": "^3.1.1", + "csstype": "^3.1.2", "prop-types": "^15.8.1" }, "engines": { @@ -3181,8 +3192,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.3", - "license": "MIT", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", + "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", "peerDependencies": { "@types/react": "*" }, @@ -3193,10 +3205,11 @@ } }, "node_modules/@mui/utils": { - "version": "5.11.9", - "license": "MIT", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.12.0.tgz", + "integrity": "sha512-RmQwgzF72p7Yr4+AAUO6j1v2uzt6wr7SWXn68KBsnfVpdOHyclCzH2lr/Xu6YOw9su4JRtdAIYfJFXsS6Cjkmw==", "dependencies": { - "@babel/runtime": "^7.20.13", + "@babel/runtime": "^7.21.0", "@types/prop-types": "^15.7.5", "@types/react-is": "^16.7.1 || ^17.0.0", "prop-types": "^15.8.1", @@ -3215,7 +3228,33 @@ }, "node_modules/@mui/utils/node_modules/react-is": { "version": "18.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/@mui/x-data-grid": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.2.1.tgz", + "integrity": "sha512-vFobFeFJslc4JRXark5SUFIzh/hwpyB8UENqUE3m0CbqKoRf4so2nkvaiK9TPsVIJbAuF6g1xyS0IhvMU2mzWg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.11.13", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "reselect": "^4.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.4.1", + "@mui/system": "^5.4.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", @@ -4122,8 +4161,9 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.6", - "license": "MIT", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -5405,10 +5445,21 @@ } }, "node_modules/@types/react-is": { - "version": "17.0.3", - "license": "MIT", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.4.tgz", + "integrity": "sha512-FLzd0K9pnaEvKz4D1vYxK9JmgQPiGk1lu23o1kqGsLeT0iPbRSF7b76+S5T9fD8aRa0B8bY7I/3DebEj+1ysBA==", "dependencies": { - "@types/react": "*" + "@types/react": "^17" + } + }, + "node_modules/@types/react-is/node_modules/@types/react": { + "version": "17.0.58", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.58.tgz", + "integrity": "sha512-c1GzVY97P0fGxwGxhYq989j4XwlcHQoto6wQISOC2v6wm3h0PORRWJFHlkRjfGsiG3y1609WdQ+J+tKxvrEd6A==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" } }, "node_modules/@types/react-transition-group": { @@ -6991,7 +7042,8 @@ }, "node_modules/clsx": { "version": "1.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "engines": { "node": ">=6" } @@ -7752,8 +7804,9 @@ "license": "MIT" }, "node_modules/csstype": { - "version": "3.1.1", - "license": "MIT" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/cycle": { "version": "1.0.3", @@ -16691,6 +16744,11 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "license": "MIT" diff --git a/package.json b/package.json index 03dfc2c..911d51e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", - "@mui/material": "^5.11.10", + "@mui/material": "^5.12.2", + "@mui/x-data-grid": "^6.2.1", "@nivo/api": "^0.74.1", "@nivo/bar": "^0.80.0", "@nivo/core": "^0.80.0", diff --git a/src/App.css b/src/App.css index d3bd3e7..82d1648 100644 --- a/src/App.css +++ b/src/App.css @@ -42,6 +42,7 @@ h2{ + @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } diff --git a/src/App.js b/src/App.js index 9fab8df..63ac446 100644 --- a/src/App.js +++ b/src/App.js @@ -129,7 +129,7 @@ if (config && isConfigured && token!==null){ } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/pages/components/activity/activity-table.js b/src/pages/components/activity/activity-table.js index b9db805..b7e171e 100644 --- a/src/pages/components/activity/activity-table.js +++ b/src/pages/components/activity/activity-table.js @@ -1,156 +1,187 @@ -import React ,{useState} from 'react'; +import React from 'react'; import { Link } from "react-router-dom"; -// import { useParams } from 'react-router-dom'; +import { Button, ButtonGroup } from "react-bootstrap"; + + +import Table from '@mui/material/Table'; +import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +import TableContainer from '@mui/material/TableContainer'; +import TableHead from '@mui/material/TableHead'; +import TableRow from '@mui/material/TableRow'; +import Collapse from '@mui/material/Collapse'; +import IconButton from '@mui/material/IconButton'; +import Box from '@mui/material/Box'; + +import AddCircleFillIcon from 'remixicon-react/AddCircleFillIcon'; +import IndeterminateCircleFillIcon from 'remixicon-react/IndeterminateCircleFillIcon'; import '../../css/activity/activity-table.css'; +function formatTotalWatchTime(seconds) { + const hours = Math.floor(seconds / 3600); // 1 hour = 3600 seconds + const minutes = Math.floor((seconds % 3600) / 60); // 1 minute = 60 seconds + let formattedTime=''; + if(hours) + { + formattedTime+=`${hours} hours`; + } + if(minutes) + { + formattedTime+=` ${minutes} minutes`; + } -function ActivityTable(props) { + return formattedTime ; +} - const [sortConfig, setSortConfig] = useState({ key: null, direction: null }); - const [currentPage, setCurrentPage] = useState(1); +function Row(data) { + const { row } = data; + const [open, setOpen] = React.useState(false); + // const classes = useRowStyles(); - const [data, setData] = useState(props.data); - - function handleSort(key) { - const direction = - sortConfig.key === key && sortConfig.direction === "ascending" - ? "descending" - : "ascending"; - setSortConfig({ key, direction }); - } - - function sortData(data, { key, direction }) { - if (!key) return data; - - const sortedData = [...data]; - - sortedData.sort((a, b) => { - if (a[key] < b[key]) return direction === "ascending" ? -1 : 1; - if (a[key] > b[key]) return direction === "ascending" ? 1 : -1; - return 0; - }); - - return sortedData; - } - - const options = { - day: "numeric", - month: "numeric", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - hour12: false, - }; + const options = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + hour12: false, + }; - const sortedData = sortData(data, sortConfig); - - const indexOfLastUser = currentPage * props.itemCount; - const indexOfFirstUser = indexOfLastUser - props.itemCount; - const currentData = sortedData.slice(indexOfFirstUser, indexOfLastUser); - const pageNumbers = []; - for (let i = 1; i <= Math.ceil(sortedData.length / props.itemCount); i++) { - pageNumbers.push(i); - } - - const handleCollapse = (itemId) => { - setData(data.map(item => { - if ((item.NowPlayingItemId+item.EpisodeId) === itemId) { - return { ...item, isCollapsed: !item.isCollapsed }; - } else { - return item; - } - })); - } - function formatTotalWatchTime(seconds) { - const hours = Math.floor(seconds / 3600); // 1 hour = 3600 seconds - const minutes = Math.floor((seconds % 3600) / 60); // 1 minute = 60 seconds - let formattedTime=''; - if(hours) - { - formattedTime+=`${hours} hours`; - } - if(minutes) - { - formattedTime+=` ${minutes} minutes`; - } - - return formattedTime ; - } - - - return ( -
- -
-
-
handleSort("UserName")}>User
-
handleSort("NowPlayingItemName")}>Title
-
handleSort("ActivityDateInserted")}>Date
-
handleSort("PlaybackDuration")}>Playback Duration
-
handleSort("results")}>Total Plays
-
+ + *': { borderBottom: 'unset' } }}> + + {if(row.results.length>1){setOpen(!open);}}} + > + {!open ? : } + + + {row.UserName} + {!row.SeriesName ? row.NowPlayingItemName : row.SeriesName+' - '+ row.NowPlayingItemName} + {row.Client} + {Intl.DateTimeFormat('en-UK', options).format(new Date(row.ActivityDateInserted))} + {formatTotalWatchTime(row.PlaybackDuration) || '0 minutes'} + {row.results.length} + + + + + - {currentData.map((item) => ( - -
handleCollapse(item.NowPlayingItemId+item.EpisodeId)}> -
-
{item.UserName}
-
{!item.SeriesName ? item.NowPlayingItemName : item.SeriesName+' - '+ item.NowPlayingItemName}
-
{Intl.DateTimeFormat('en-UK', options).format(new Date(item.ActivityDateInserted))}
-
{formatTotalWatchTime(item.PlaybackDuration) || '0 sec'}
-
{item.results.length+1}
-
-
- {item.results.map((sub_item,index) => ( - -
-
{sub_item.UserName}
-
{!sub_item.SeriesName ? sub_item.NowPlayingItemName : sub_item.SeriesName+' - '+ sub_item.NowPlayingItemName}
-
{Intl.DateTimeFormat('en-UK', options).format(new Date(sub_item.ActivityDateInserted))}
-
-
1
-
- ))} -
-
- ))} -
- - - - - - {props.itemCount>0 ? - -
- - - - -
{`Page ${currentPage} of ${pageNumbers.length}`}
- - - - -
- :<> - - } -
+ + + + Username + Title + Client + Date + Playback Duration + Plays + + + + {row.results.map((resultRow) => ( + + + {resultRow.UserName} + {!resultRow.SeriesName ? resultRow.NowPlayingItemName : resultRow.SeriesName+' - '+ resultRow.NowPlayingItemName} + {resultRow.Client} + {Intl.DateTimeFormat('en-UK', options).format(new Date(resultRow.ActivityDateInserted))} + {formatTotalWatchTime(resultRow.PlaybackDuration) || '0 minutes'} + 1 + + ))} + +
+ + + + + ); } -export default ActivityTable; + +export default function ActivityTable(props) { + const [page, setPage] = React.useState(0); + const [rowsPerPage, setRowsPerPage] = React.useState(10); + + const handleChangePage = (event, newPage) => { + setPage(newPage); + }; + + + if(rowsPerPage!==props.itemCount) + { + setRowsPerPage(props.itemCount); + setPage(0); + } + + + const handleNextPageClick = () => { + setPage((prevPage) => prevPage + 1); + }; + + const handlePreviousPageClick = () => { + setPage((prevPage) => prevPage - 1); + }; + + return ( + <> + + + + + + Username + Title + Client + Date + Playback Duration + Plays + + + + {props.data + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row) => ( + + ))} + {props.data.length===0 ? :''} + + +
No Activity Found
+
+ +
+ + + + + +
{`${page *rowsPerPage + 1}-${Math.min((page * rowsPerPage+ 1 ) + (rowsPerPage - 1),props.data.length)} of ${props.data.length}`}
+ + + + +
+
+ + + ); +} \ No newline at end of file diff --git a/src/pages/components/general/last-watched-card.js b/src/pages/components/general/last-watched-card.js index 23278a3..4d2613a 100644 --- a/src/pages/components/general/last-watched-card.js +++ b/src/pages/components/general/last-watched-card.js @@ -33,7 +33,7 @@ function LastWatchedCard(props) { const [loaded, setLoaded] = useState(false); return (
- +
{loaded ? null : }