diff --git a/package-lock.json b/package-lock.json index f3d690680e3..fa81fe3544c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "@jellyfin/sdk": "0.0.0-unstable.202408270502", "@mui/icons-material": "5.15.19", "@mui/material": "5.15.19", - "@mui/x-data-grid": "7.6.1", + "@mui/x-date-pickers": "7.14.0", "@react-hook/resize-observer": "2.0.2", "@tanstack/react-query": "5.51.24", "@tanstack/react-query-devtools": "5.51.24", @@ -48,6 +48,7 @@ "lodash-es": "4.17.21", "markdown-it": "14.1.0", "material-design-icons-iconfont": "6.7.0", + "material-react-table": "2.13.1", "native-promise-only": "0.8.1", "pdfjs-dist": "3.11.174", "react": "18.3.1", @@ -2006,10 +2007,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "license": "MIT", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -5418,12 +5418,12 @@ "license": "MIT" }, "node_modules/@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.6", "prop-types": "^15.8.1" }, "engines": { @@ -5444,9 +5444,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -5475,15 +5475,15 @@ } }, "node_modules/@mui/system": { - "version": "5.15.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", + "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", - "@mui/styled-engine": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.6", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -5514,9 +5514,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -5527,14 +5527,16 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^18.3.1" }, "engines": { "node": ">=12.0.0" @@ -5554,22 +5556,22 @@ } }, "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, - "node_modules/@mui/x-data-grid": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.6.1.tgz", - "integrity": "sha512-ZUQqSvmJgNQAgwLqVp/XUgNgKFb3zdsBQTbYCagjAK7Saq3iPDJkTb7FNSyT8UN0G6Kqogxgd9fKJW4L4ku1zQ==", - "license": "MIT", + "node_modules/@mui/x-date-pickers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.14.0.tgz", + "integrity": "sha512-3xI3xYVxqPU4//KfE4FcR+Zs7UT4kkDPvA+IDOcQdRUyVwmcXCjBuJZgKgJMqSCNK/KIJZQQrpmy5XGHOKTbdA==", "dependencies": { - "@babel/runtime": "^7.24.6", - "@mui/system": "^5.15.15", - "@mui/utils": "^5.15.14", + "@babel/runtime": "^7.25.0", + "@mui/system": "^5.16.7", + "@mui/utils": "^5.16.6", + "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "reselect": "^4.1.8" + "react-transition-group": "^4.4.5" }, "engines": { "node": ">=14.0.0" @@ -5579,9 +5581,47 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } } }, "node_modules/@nodelib/fs.scandir": { @@ -6254,6 +6294,21 @@ "node": ">=10" } }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.15.1.tgz", + "integrity": "sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==", + "dependencies": { + "remove-accents": "0.5.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/query-core": { "version": "5.51.24", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.24.tgz", @@ -6307,6 +6362,62 @@ "react": "^18 || ^19" } }, + "node_modules/@tanstack/react-table": { + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz", + "integrity": "sha512-MtgPZc4y+cCRtU16y1vh1myuyZ2OdkWgMEBzyjYsoMWMicKZGZvcDnub3Zwb6XF2pj9iRMvm1SO1n57lS0vXLw==", + "dependencies": { + "@tanstack/table-core": "8.19.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.3.tgz", + "integrity": "sha512-9ICwbDUUzN99CJIGc373i8NLoj6zFTKI2Hlcmo0+lCSAhPQ5mxq4dGOMKmLYoEFyHcGQ64Bd6ZVbnPpM6lNK5w==", + "dependencies": { + "@tanstack/virtual-core": "3.8.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.19.3.tgz", + "integrity": "sha512-IqREj9ADoml9zCAouIG/5kCGoyIxPFdqdyoxis9FisXFi5vT+iYfEfLosq4xkU/iDbMcEuAj+X8dWRLvKYDNoQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.8.3.tgz", + "integrity": "sha512-vd2A2TnM5lbnWZnHi9B+L2gPtkSeOtJOAw358JqokIH1+v2J7vUAzFVPwB/wrye12RFOurffXu33plm4uQ+JBQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -6585,9 +6696,9 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/qs": { "version": "6.9.7", @@ -6630,9 +6741,9 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", "dependencies": { "@types/react": "*" } @@ -13202,6 +13313,15 @@ "resolved": "https://registry.npmjs.org/headroom.js/-/headroom.js-0.12.0.tgz", "integrity": "sha512-iXnAafUm3FdzfJ91uixLws2hkKI1jC8bAKK/pt7XYr8Ie1jO7xbK48Ycpl9tUPyBgkzuj1p/PhJS0fy4E/5anA==" }, + "node_modules/highlight-words": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/highlight-words/-/highlight-words-1.2.2.tgz", + "integrity": "sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ==", + "engines": { + "node": ">= 16", + "npm": ">= 8" + } + }, "node_modules/history": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", @@ -15471,6 +15591,33 @@ "resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.7.0.tgz", "integrity": "sha512-lSj71DgVv20kO0kGbs42icDzbRot61gEDBLQACzkUuznRQBUYmbxzEkGU6dNBb5fRWHMaScYlAXX96HQ4/cJWA==" }, + "node_modules/material-react-table": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/material-react-table/-/material-react-table-2.13.1.tgz", + "integrity": "sha512-3iWwCa24ogxwllP4+W11euR/GV6f5wQE5FEilJ72/H3hDYHgsN+XehANytaG0G7/qy/OWYE7oXkcsRUU35I/iA==", + "dependencies": { + "@tanstack/match-sorter-utils": "8.15.1", + "@tanstack/react-table": "8.19.3", + "@tanstack/react-virtual": "3.8.3", + "highlight-words": "1.2.2" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kevinvandy" + }, + "peerDependencies": { + "@emotion/react": ">=11.11", + "@emotion/styled": ">=11.11", + "@mui/icons-material": ">=5.11", + "@mui/material": ">=5.13", + "@mui/x-date-pickers": ">=6.15.0", + "react": ">=17.0", + "react-dom": ">=17.0" + } + }, "node_modules/mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -19948,6 +20095,11 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -20003,11 +20155,6 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "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", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -28243,9 +28390,9 @@ "dev": true }, "@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "requires": { "regenerator-runtime": "^0.14.0" } @@ -29785,19 +29932,19 @@ } }, "@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", "requires": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", + "@mui/utils": "^5.16.6", "prop-types": "^15.8.1" } }, "@mui/styled-engine": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", "requires": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -29806,55 +29953,58 @@ } }, "@mui/system": { - "version": "5.15.15", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", + "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", "requires": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", - "@mui/styled-engine": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.6", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" } }, "@mui/types": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", "requires": {} }, "@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", "requires": { "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^18.3.1" }, "dependencies": { "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" } } }, - "@mui/x-data-grid": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.6.1.tgz", - "integrity": "sha512-ZUQqSvmJgNQAgwLqVp/XUgNgKFb3zdsBQTbYCagjAK7Saq3iPDJkTb7FNSyT8UN0G6Kqogxgd9fKJW4L4ku1zQ==", + "@mui/x-date-pickers": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.14.0.tgz", + "integrity": "sha512-3xI3xYVxqPU4//KfE4FcR+Zs7UT4kkDPvA+IDOcQdRUyVwmcXCjBuJZgKgJMqSCNK/KIJZQQrpmy5XGHOKTbdA==", "requires": { - "@babel/runtime": "^7.24.6", - "@mui/system": "^5.15.15", - "@mui/utils": "^5.15.14", + "@babel/runtime": "^7.25.0", + "@mui/system": "^5.16.7", + "@mui/utils": "^5.16.6", + "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "reselect": "^4.1.8" + "react-transition-group": "^4.4.5" } }, "@nodelib/fs.scandir": { @@ -30266,6 +30416,14 @@ } } }, + "@tanstack/match-sorter-utils": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.15.1.tgz", + "integrity": "sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==", + "requires": { + "remove-accents": "0.5.0" + } + }, "@tanstack/query-core": { "version": "5.51.24", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.24.tgz", @@ -30292,6 +30450,32 @@ "@tanstack/query-devtools": "5.51.16" } }, + "@tanstack/react-table": { + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz", + "integrity": "sha512-MtgPZc4y+cCRtU16y1vh1myuyZ2OdkWgMEBzyjYsoMWMicKZGZvcDnub3Zwb6XF2pj9iRMvm1SO1n57lS0vXLw==", + "requires": { + "@tanstack/table-core": "8.19.3" + } + }, + "@tanstack/react-virtual": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.3.tgz", + "integrity": "sha512-9ICwbDUUzN99CJIGc373i8NLoj6zFTKI2Hlcmo0+lCSAhPQ5mxq4dGOMKmLYoEFyHcGQ64Bd6ZVbnPpM6lNK5w==", + "requires": { + "@tanstack/virtual-core": "3.8.3" + } + }, + "@tanstack/table-core": { + "version": "8.19.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.19.3.tgz", + "integrity": "sha512-IqREj9ADoml9zCAouIG/5kCGoyIxPFdqdyoxis9FisXFi5vT+iYfEfLosq4xkU/iDbMcEuAj+X8dWRLvKYDNoQ==" + }, + "@tanstack/virtual-core": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.8.3.tgz", + "integrity": "sha512-vd2A2TnM5lbnWZnHi9B+L2gPtkSeOtJOAw358JqokIH1+v2J7vUAzFVPwB/wrye12RFOurffXu33plm4uQ+JBQ==" + }, "@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -30555,9 +30739,9 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "@types/qs": { "version": "6.9.7", @@ -30599,9 +30783,9 @@ } }, "@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", "requires": { "@types/react": "*" } @@ -35411,6 +35595,11 @@ "resolved": "https://registry.npmjs.org/headroom.js/-/headroom.js-0.12.0.tgz", "integrity": "sha512-iXnAafUm3FdzfJ91uixLws2hkKI1jC8bAKK/pt7XYr8Ie1jO7xbK48Ycpl9tUPyBgkzuj1p/PhJS0fy4E/5anA==" }, + "highlight-words": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/highlight-words/-/highlight-words-1.2.2.tgz", + "integrity": "sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ==" + }, "history": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", @@ -37050,6 +37239,17 @@ "resolved": "https://registry.npmjs.org/material-design-icons-iconfont/-/material-design-icons-iconfont-6.7.0.tgz", "integrity": "sha512-lSj71DgVv20kO0kGbs42icDzbRot61gEDBLQACzkUuznRQBUYmbxzEkGU6dNBb5fRWHMaScYlAXX96HQ4/cJWA==" }, + "material-react-table": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/material-react-table/-/material-react-table-2.13.1.tgz", + "integrity": "sha512-3iWwCa24ogxwllP4+W11euR/GV6f5wQE5FEilJ72/H3hDYHgsN+XehANytaG0G7/qy/OWYE7oXkcsRUU35I/iA==", + "requires": { + "@tanstack/match-sorter-utils": "8.15.1", + "@tanstack/react-table": "8.19.3", + "@tanstack/react-virtual": "3.8.3", + "highlight-words": "1.2.2" + } + }, "mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -39886,6 +40086,11 @@ "mdast-util-to-markdown": "^0.6.0" } }, + "remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -39929,11 +40134,6 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" - }, "resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", diff --git a/package.json b/package.json index 74f63f3338e..514787d9acd 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "@jellyfin/sdk": "0.0.0-unstable.202408270502", "@mui/icons-material": "5.15.19", "@mui/material": "5.15.19", - "@mui/x-data-grid": "7.6.1", + "@mui/x-date-pickers": "7.14.0", "@react-hook/resize-observer": "2.0.2", "@tanstack/react-query": "5.51.24", "@tanstack/react-query-devtools": "5.51.24", @@ -112,6 +112,7 @@ "lodash-es": "4.17.21", "markdown-it": "14.1.0", "material-design-icons-iconfont": "6.7.0", + "material-react-table": "2.13.1", "native-promise-only": "0.8.1", "pdfjs-dist": "3.11.174", "react": "18.3.1", diff --git a/src/apps/dashboard/components/dataGrid/GridActionsCellLink.tsx b/src/apps/dashboard/components/dataGrid/GridActionsCellLink.tsx deleted file mode 100644 index faf7619c9e2..00000000000 --- a/src/apps/dashboard/components/dataGrid/GridActionsCellLink.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { type RefAttributes } from 'react'; -import { Link } from 'react-router-dom'; -import { GridActionsCellItem, type GridActionsCellItemProps } from '@mui/x-data-grid'; - -type GridActionsCellLinkProps = { to: string } & GridActionsCellItemProps & RefAttributes; - -/** - * Link component to use in mui's data-grid action column due to a current bug with passing props to custom link components. - * @see https://github.com/mui/mui-x/issues/4654 - */ -const GridActionsCellLink = ({ to, ...props }: GridActionsCellLinkProps) => ( - - - -); - -export default GridActionsCellLink; diff --git a/src/hooks/useLogEntries.tsx b/src/apps/dashboard/features/activity/api/useLogEntries.ts similarity index 62% rename from src/hooks/useLogEntries.tsx rename to src/apps/dashboard/features/activity/api/useLogEntries.ts index 748b8f656b6..b0f1be52338 100644 --- a/src/hooks/useLogEntries.tsx +++ b/src/apps/dashboard/features/activity/api/useLogEntries.ts @@ -1,18 +1,20 @@ import type { ActivityLogApiGetLogEntriesRequest } from '@jellyfin/sdk/lib/generated-client'; import type { AxiosRequestConfig } from 'axios'; +import type { Api } from '@jellyfin/sdk'; import { getActivityLogApi } from '@jellyfin/sdk/lib/utils/api/activity-log-api'; import { useQuery } from '@tanstack/react-query'; -import { JellyfinApiContext, useApi } from './useApi'; +import { useApi } from 'hooks/useApi'; -const fetchGetLogEntries = async ( - currentApi: JellyfinApiContext, - requestParams: ActivityLogApiGetLogEntriesRequest, +const fetchLogEntries = async ( + api?: Api, + requestParams?: ActivityLogApiGetLogEntriesRequest, options?: AxiosRequestConfig ) => { - const { api } = currentApi; - - if (!api) return; + if (!api) { + console.warn('[fetchLogEntries] No API instance available'); + return; + } const response = await getActivityLogApi(api).getLogEntries(requestParams, { signal: options?.signal @@ -24,10 +26,11 @@ const fetchGetLogEntries = async ( export const useLogEntires = ( requestParams: ActivityLogApiGetLogEntriesRequest ) => { - const currentApi = useApi(); + const { api } = useApi(); return useQuery({ queryKey: ['LogEntries', requestParams], queryFn: ({ signal }) => - fetchGetLogEntries(currentApi, requestParams, { signal }) + fetchLogEntries(api, requestParams, { signal }), + enabled: !!api }); }; diff --git a/src/apps/dashboard/features/activity/components/ActionsCell.tsx b/src/apps/dashboard/features/activity/components/ActionsCell.tsx new file mode 100644 index 00000000000..0f006132704 --- /dev/null +++ b/src/apps/dashboard/features/activity/components/ActionsCell.tsx @@ -0,0 +1,22 @@ +import IconButton from '@mui/material/IconButton/IconButton'; +import PermMedia from '@mui/icons-material/PermMedia'; +import React, { type FC } from 'react'; +import { Link } from 'react-router-dom'; + +import type { ActivityLogEntryCell } from 'apps/dashboard/features/activity/types/ActivityLogEntryCell'; +import globalize from 'lib/globalize'; + +const ActionsCell: FC = ({ row }) => ( + row.original.ItemId ? ( + + + + ) : undefined +); + +export default ActionsCell; diff --git a/src/apps/dashboard/features/activity/components/LogLevelCell.tsx b/src/apps/dashboard/features/activity/components/LogLevelCell.tsx new file mode 100644 index 00000000000..4728e7a2bf7 --- /dev/null +++ b/src/apps/dashboard/features/activity/components/LogLevelCell.tsx @@ -0,0 +1,14 @@ +import type { LogLevel } from '@jellyfin/sdk/lib/generated-client/models/log-level'; +import React, { type FC } from 'react'; + +import { ActivityLogEntryCell } from '../types/ActivityLogEntryCell'; +import LogLevelChip from './LogLevelChip'; + +const LogLevelCell: FC = ({ cell }) => { + const level = cell.getValue(); + return level ? ( + + ) : undefined; +}; + +export default LogLevelCell; diff --git a/src/apps/dashboard/components/activityTable/LogLevelChip.tsx b/src/apps/dashboard/features/activity/components/LogLevelChip.tsx similarity index 100% rename from src/apps/dashboard/components/activityTable/LogLevelChip.tsx rename to src/apps/dashboard/features/activity/components/LogLevelChip.tsx diff --git a/src/apps/dashboard/components/activityTable/OverviewCell.tsx b/src/apps/dashboard/features/activity/components/OverviewCell.tsx similarity index 87% rename from src/apps/dashboard/components/activityTable/OverviewCell.tsx rename to src/apps/dashboard/features/activity/components/OverviewCell.tsx index 69702de5f6d..a0c453b0753 100644 --- a/src/apps/dashboard/components/activityTable/OverviewCell.tsx +++ b/src/apps/dashboard/features/activity/components/OverviewCell.tsx @@ -1,12 +1,14 @@ -import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry'; import Info from '@mui/icons-material/Info'; import Box from '@mui/material/Box'; import ClickAwayListener from '@mui/material/ClickAwayListener'; import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; -import React, { FC, useCallback, useState } from 'react'; +import React, { type FC, useCallback, useState } from 'react'; -const OverviewCell: FC = ({ Overview, ShortOverview }) => { +import type { ActivityLogEntryCell } from '../types/ActivityLogEntryCell'; + +const OverviewCell: FC = ({ row }) => { + const { ShortOverview, Overview } = row.original; const displayValue = ShortOverview ?? Overview; const [ open, setOpen ] = useState(false); diff --git a/src/apps/dashboard/features/activity/components/UserAvatarButton.tsx b/src/apps/dashboard/features/activity/components/UserAvatarButton.tsx new file mode 100644 index 00000000000..91f126e92cf --- /dev/null +++ b/src/apps/dashboard/features/activity/components/UserAvatarButton.tsx @@ -0,0 +1,27 @@ +import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto'; +import IconButton from '@mui/material/IconButton/IconButton'; +import React, { type FC } from 'react'; +import { Link } from 'react-router-dom'; + +import UserAvatar from 'components/UserAvatar'; + +interface UserAvatarButtonProps { + user?: UserDto +} + +const UserAvatarButton: FC = ({ user }) => ( + user?.Id ? ( + + + + ) : undefined +); + +export default UserAvatarButton; diff --git a/src/apps/dashboard/features/activity/types/ActivityLogEntryCell.ts b/src/apps/dashboard/features/activity/types/ActivityLogEntryCell.ts new file mode 100644 index 00000000000..556f65685b5 --- /dev/null +++ b/src/apps/dashboard/features/activity/types/ActivityLogEntryCell.ts @@ -0,0 +1,7 @@ +import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry'; +import type { MRT_Cell, MRT_Row } from 'material-react-table'; + +export interface ActivityLogEntryCell { + cell: MRT_Cell + row: MRT_Row +} diff --git a/src/apps/dashboard/routes/activity.tsx b/src/apps/dashboard/routes/activity.tsx index e5f102c1311..630cd4ba95c 100644 --- a/src/apps/dashboard/routes/activity.tsx +++ b/src/apps/dashboard/routes/activity.tsx @@ -1,34 +1,34 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry'; import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto'; -import PermMedia from '@mui/icons-material/PermMedia'; import Box from '@mui/material/Box'; -import IconButton from '@mui/material/IconButton'; import ToggleButton from '@mui/material/ToggleButton'; import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import Typography from '@mui/material/Typography'; -import { DataGrid, type GridColDef } from '@mui/x-data-grid'; -import { Link, useSearchParams } from 'react-router-dom'; - +import { type MRT_ColumnDef, MaterialReactTable, useMaterialReactTable } from 'material-react-table'; +import { useSearchParams } from 'react-router-dom'; + +import { useLogEntires } from 'apps/dashboard/features/activity/api/useLogEntries'; +import ActionsCell from 'apps/dashboard/features/activity/components/ActionsCell'; +import LogLevelCell from 'apps/dashboard/features/activity/components/LogLevelCell'; +import OverviewCell from 'apps/dashboard/features/activity/components/OverviewCell'; +import UserAvatarButton from 'apps/dashboard/features/activity/components/UserAvatarButton'; +import type { ActivityLogEntryCell } from 'apps/dashboard/features/activity/types/ActivityLogEntryCell'; import Page from 'components/Page'; -import UserAvatar from 'components/UserAvatar'; -import { useLogEntires } from 'hooks/useLogEntries'; import { useUsers } from 'hooks/useUsers'; -import { parseISO8601Date, toLocaleDateString, toLocaleTimeString } from 'scripts/datetime'; +import { parseISO8601Date, toLocaleString } from 'scripts/datetime'; import globalize from 'lib/globalize'; import { toBoolean } from 'utils/string'; -import LogLevelChip from '../components/activityTable/LogLevelChip'; -import OverviewCell from '../components/activityTable/OverviewCell'; -import GridActionsCellLink from '../components/dataGrid/GridActionsCellLink'; +type UsersRecords = Record; const DEFAULT_PAGE_SIZE = 25; const VIEW_PARAM = 'useractivity'; const enum ActivityView { - All, - User, - System + All = 'All', + User = 'User', + System = 'System' } const getActivityView = (param: string | null) => { @@ -37,7 +37,11 @@ const getActivityView = (param: string | null) => { return ActivityView.System; }; -const getRowId = (row: ActivityLogEntry) => row.Id ?? -1; +const getUserCell = (users: UsersRecords) => function UserCell({ row }: ActivityLogEntryCell) { + return ( + + ); +}; const Activity = () => { const [ searchParams, setSearchParams ] = useSearchParams(); @@ -45,14 +49,13 @@ const Activity = () => { const [ activityView, setActivityView ] = useState( getActivityView(searchParams.get(VIEW_PARAM))); - const [ paginationModel, setPaginationModel ] = useState({ - page: 0, + const [ pagination, setPagination ] = useState({ + pageIndex: 0, pageSize: DEFAULT_PAGE_SIZE }); const { data: usersData, isLoading: isUsersLoading } = useUsers(); - type UsersRecords = Record; const users: UsersRecords = useMemo(() => { if (!usersData) return {}; @@ -67,107 +70,79 @@ const Activity = () => { }, {}); }, [usersData]); + const UserCell = getUserCell(users); + const activityParams = useMemo(() => ({ - startIndex: paginationModel.page * paginationModel.pageSize, - limit: paginationModel.pageSize, + startIndex: pagination.pageIndex * pagination.pageSize, + limit: pagination.pageSize, hasUserId: activityView !== ActivityView.All ? activityView === ActivityView.User : undefined - }), [activityView, paginationModel.page, paginationModel.pageSize]); + }), [activityView, pagination.pageIndex, pagination.pageSize]); const { data: logEntries, isLoading: isLogEntriesLoading } = useLogEntires(activityParams); const isLoading = isUsersLoading || isLogEntriesLoading; - const userColDef: GridColDef[] = activityView !== ActivityView.System ? [ - { - field: 'User', - headerName: globalize.translate('LabelUser'), - width: 60, - valueGetter: ( value, row ) => users[row.UserId]?.Name, - renderCell: ({ row }) => ( - - - - ) - } - ] : []; + const userColumn: MRT_ColumnDef[] = useMemo(() => + (activityView === ActivityView.System) ? [] : [{ + id: 'User', + accessorFn: row => row.UserId && users[row.UserId]?.Name, + header: globalize.translate('LabelUser'), + size: 75, + Cell: UserCell, + enableResizing: false, + muiTableBodyCellProps: { + align: 'center' + } + }], [ activityView, users, UserCell ]); - const columns: GridColDef[] = [ + const columns = useMemo[]>(() => [ { - field: 'Date', - headerName: globalize.translate('LabelDate'), - width: 90, - type: 'date', - valueGetter: ( value ) => parseISO8601Date(value), - valueFormatter: ( value ) => toLocaleDateString(value) + id: 'Date', + accessorFn: row => parseISO8601Date(row.Date), + header: globalize.translate('LabelTime'), + size: 160, + Cell: ({ cell }) => toLocaleString(cell.getValue()) }, { - field: 'Time', - headerName: globalize.translate('LabelTime'), - width: 100, - type: 'dateTime', - valueGetter: ( value, row ) => parseISO8601Date(row.Date), - valueFormatter: ( value ) => toLocaleTimeString(value) - }, - { - field: 'Severity', - headerName: globalize.translate('LabelLevel'), - width: 110, - renderCell: ({ value }) => ( - value ? ( - - ) : undefined - ) + accessorKey: 'Severity', + header: globalize.translate('LabelLevel'), + size: 90, + Cell: LogLevelCell, + enableResizing: false, + muiTableBodyCellProps: { + align: 'center' + } }, - ...userColDef, + ...userColumn, { - field: 'Name', - headerName: globalize.translate('LabelName'), - width: 300 + accessorKey: 'Name', + header: globalize.translate('LabelName'), + size: 270 }, { - field: 'Overview', - headerName: globalize.translate('LabelOverview'), - width: 200, - valueGetter: ( value, row ) => row.ShortOverview ?? row.Overview, - renderCell: ({ row }) => ( - - ) + id: 'Overview', + accessorFn: row => row.ShortOverview || row.Overview, + header: globalize.translate('LabelOverview'), + size: 170, + Cell: OverviewCell }, { - field: 'Type', - headerName: globalize.translate('LabelType'), - width: 180 + accessorKey: 'Type', + header: globalize.translate('LabelType'), + size: 150 }, { - field: 'actions', - type: 'actions', - width: 50, - getActions: ({ row }) => { - const actions = []; - - if (row.ItemId) { - actions.push( - } - label={globalize.translate('LabelMediaDetails')} - title={globalize.translate('LabelMediaDetails')} - to={`/details?id=${row.ItemId}`} - /> - ); - } - - return actions; - } + id: 'Actions', + accessorFn: row => row.ItemId, + header: '', + size: 60, + Cell: ActionsCell, + enableColumnActions: false, + enableColumnFilter: false, + enableResizing: false, + enableSorting: false } - ]; + ], [ userColumn ]); const onViewChange = useCallback((_e: React.MouseEvent, newView: ActivityView | null) => { if (newView !== null) { @@ -187,6 +162,58 @@ const Activity = () => { } }, [ activityView, searchParams, setSearchParams ]); + const table = useMaterialReactTable({ + columns, + data: logEntries?.Items || [], + + // Enable custom features + enableColumnPinning: true, + enableColumnResizing: true, + + // Sticky header/footer + enableStickyFooter: true, + enableStickyHeader: true, + muiTableContainerProps: { + sx: { + maxHeight: 'calc(100% - 7rem)' // 2 x 3.5rem for header and footer + } + }, + + // State + initialState: { + density: 'compact' + }, + state: { + isLoading, + pagination + }, + + // Server pagination + manualPagination: true, + onPaginationChange: setPagination, + rowCount: logEntries?.TotalRecordCount || 0, + + // Custom toolbar contents + renderTopToolbarCustomActions: () => ( + + + {globalize.translate('All')} + + + {globalize.translate('LabelUser')} + + + {globalize.translate('LabelSystem')} + + + ) + }); + return ( { > - - - {globalize.translate('HeaderActivity')} - - - - - {globalize.translate('All')} - - - {globalize.translate('LabelUser')} - - - {globalize.translate('LabelSystem')} - - + + {globalize.translate('HeaderActivity')} + - + ); diff --git a/src/hooks/useUsers.tsx b/src/hooks/useUsers.ts similarity index 65% rename from src/hooks/useUsers.tsx rename to src/hooks/useUsers.ts index 0a49c76d718..cc62d6b2c36 100644 --- a/src/hooks/useUsers.tsx +++ b/src/hooks/useUsers.ts @@ -1,18 +1,20 @@ import type { AxiosRequestConfig } from 'axios'; +import type { Api } from '@jellyfin/sdk'; import type { UserApiGetUsersRequest } from '@jellyfin/sdk/lib/generated-client'; import { getUserApi } from '@jellyfin/sdk/lib/utils/api/user-api'; import { useQuery } from '@tanstack/react-query'; -import { type JellyfinApiContext, useApi } from './useApi'; +import { useApi } from './useApi'; -export const fetchGetUsers = async ( - currentApi: JellyfinApiContext, +const fetchUsers = async ( + api?: Api, requestParams?: UserApiGetUsersRequest, options?: AxiosRequestConfig ) => { - const { api } = currentApi; - - if (!api) return; + if (!api) { + console.warn('[fetchUsers] No API instance available'); + return; + } const response = await getUserApi(api).getUsers(requestParams, { signal: options?.signal @@ -22,10 +24,11 @@ export const fetchGetUsers = async ( }; export const useUsers = (requestParams?: UserApiGetUsersRequest) => { - const currentApi = useApi(); + const { api } = useApi(); return useQuery({ queryKey: ['Users'], queryFn: ({ signal }) => - fetchGetUsers(currentApi, requestParams, { signal }) + fetchUsers(api, requestParams, { signal }), + enabled: !!api }); }; diff --git a/webpack.common.js b/webpack.common.js index 46e372da347..efda98699a9 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -185,13 +185,18 @@ const config = { include: [ path.resolve(__dirname, 'node_modules/@jellyfin/libass-wasm'), path.resolve(__dirname, 'node_modules/@jellyfin/sdk'), - path.resolve(__dirname, 'node_modules/@mui/x-data-grid'), + path.resolve(__dirname, 'node_modules/@mui/x-date-pickers'), path.resolve(__dirname, 'node_modules/@react-hook/latest'), path.resolve(__dirname, 'node_modules/@react-hook/passive-layout-effect'), path.resolve(__dirname, 'node_modules/@react-hook/resize-observer'), path.resolve(__dirname, 'node_modules/@remix-run/router'), + path.resolve(__dirname, 'node_modules/@tanstack/match-sorter-utils'), path.resolve(__dirname, 'node_modules/@tanstack/query-core'), path.resolve(__dirname, 'node_modules/@tanstack/react-query'), + path.resolve(__dirname, 'node_modules/@tanstack/react-table'), + path.resolve(__dirname, 'node_modules/@tanstack/react-virtual'), + path.resolve(__dirname, 'node_modules/@tanstack/table-core'), + path.resolve(__dirname, 'node_modules/@tanstack/virtual-core'), path.resolve(__dirname, 'node_modules/@uupaa/dynamic-import-polyfill'), path.resolve(__dirname, 'node_modules/axios'), path.resolve(__dirname, 'node_modules/blurhash'), @@ -200,14 +205,17 @@ const config = { path.resolve(__dirname, 'node_modules/dom7'), path.resolve(__dirname, 'node_modules/epubjs'), path.resolve(__dirname, 'node_modules/flv.js'), + path.resolve(__dirname, 'node_modules/highlight-words'), path.resolve(__dirname, 'node_modules/libarchive.js'), path.resolve(__dirname, 'node_modules/linkify-it'), path.resolve(__dirname, 'node_modules/markdown-it'), + path.resolve(__dirname, 'node_modules/material-react-table'), path.resolve(__dirname, 'node_modules/mdurl'), path.resolve(__dirname, 'node_modules/punycode'), path.resolve(__dirname, 'node_modules/react-blurhash'), path.resolve(__dirname, 'node_modules/react-lazy-load-image-component'), path.resolve(__dirname, 'node_modules/react-router'), + path.resolve(__dirname, 'node_modules/remove-accents'), path.resolve(__dirname, 'node_modules/screenfull'), path.resolve(__dirname, 'node_modules/ssr-window'), path.resolve(__dirname, 'node_modules/swiper'),