summaryrefslogtreecommitdiffhomepage
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/package-lock.json747
-rw-r--r--frontend/package.json14
-rw-r--r--frontend/postcss.config.cjs14
-rw-r--r--frontend/src/App/Header.module.scss9
-rw-r--r--frontend/src/App/Header.tsx67
-rw-r--r--frontend/src/App/Navbar.module.scss56
-rw-r--r--frontend/src/App/Navbar.tsx114
-rw-r--r--frontend/src/App/ThemeLoader.tsx39
-rw-r--r--frontend/src/App/ThemeProvider.tsx61
-rw-r--r--frontend/src/App/index.tsx18
-rw-r--r--frontend/src/App/theme.tsx87
-rw-r--r--frontend/src/Router/index.tsx4
-rw-r--r--frontend/src/assets/_bazarr.scss40
-rw-r--r--frontend/src/assets/_mantine.scss61
-rw-r--r--frontend/src/assets/_variables.module.scss18
-rw-r--r--frontend/src/assets/action_icon.module.scss14
-rw-r--r--frontend/src/assets/app_shell.module.scss5
-rw-r--r--frontend/src/assets/badge.module.scss8
-rw-r--r--frontend/src/assets/button.module.scss12
-rw-r--r--frontend/src/components/Search.module.scss9
-rw-r--r--frontend/src/components/Search.tsx80
-rw-r--r--frontend/src/components/StateIcon.tsx12
-rw-r--r--frontend/src/components/SubtitleToolsMenu.tsx6
-rw-r--r--frontend/src/components/bazarr/AudioList.tsx2
-rw-r--r--frontend/src/components/bazarr/Language.test.tsx18
-rw-r--r--frontend/src/components/bazarr/Language.tsx2
-rw-r--r--frontend/src/components/forms/FrameRateForm.tsx8
-rw-r--r--frontend/src/components/forms/ItemEditForm.tsx2
-rw-r--r--frontend/src/components/forms/MovieUploadForm.module.scss0
-rw-r--r--frontend/src/components/forms/MovieUploadForm.tsx22
-rw-r--r--frontend/src/components/forms/ProfileEditForm.module.scss5
-rw-r--r--frontend/src/components/forms/ProfileEditForm.tsx16
-rw-r--r--frontend/src/components/forms/SeriesUploadForm.tsx22
-rw-r--r--frontend/src/components/forms/SyncSubtitleForm.tsx45
-rw-r--r--frontend/src/components/forms/TimeOffsetForm.tsx2
-rw-r--r--frontend/src/components/index.tsx2
-rw-r--r--frontend/src/components/inputs/Action.test.tsx8
-rw-r--r--frontend/src/components/inputs/ChipInput.test.tsx10
-rw-r--r--frontend/src/components/inputs/ChipInput.tsx50
-rw-r--r--frontend/src/components/inputs/DropContent.module.scss4
-rw-r--r--frontend/src/components/inputs/DropContent.tsx20
-rw-r--r--frontend/src/components/inputs/FileBrowser.tsx27
-rw-r--r--frontend/src/components/inputs/Selector.test.tsx35
-rw-r--r--frontend/src/components/inputs/Selector.tsx38
-rw-r--r--frontend/src/components/modals/ManualSearchModal.tsx24
-rw-r--r--frontend/src/components/tables/BaseTable.module.scss9
-rw-r--r--frontend/src/components/tables/BaseTable.tsx61
-rw-r--r--frontend/src/components/tables/GroupTable.tsx22
-rw-r--r--frontend/src/components/tables/PageControl.tsx2
-rw-r--r--frontend/src/components/toolbox/Button.tsx2
-rw-r--r--frontend/src/components/toolbox/Toolbox.module.scss9
-rw-r--r--frontend/src/components/toolbox/Toolbox.tsx (renamed from frontend/src/components/toolbox/index.tsx)15
-rw-r--r--frontend/src/constants.ts8
-rw-r--r--frontend/src/modules/socketio/reducer.ts2
-rw-r--r--frontend/src/modules/task/index.ts2
-rw-r--r--frontend/src/modules/task/notification.ts14
-rw-r--r--frontend/src/pages/Authentication.tsx2
-rw-r--r--frontend/src/pages/Blacklist/Movies/table.tsx4
-rw-r--r--frontend/src/pages/Blacklist/Series/table.tsx4
-rw-r--r--frontend/src/pages/Episodes/index.tsx4
-rw-r--r--frontend/src/pages/Episodes/table.tsx11
-rw-r--r--frontend/src/pages/History/Movies/index.tsx4
-rw-r--r--frontend/src/pages/History/Series/index.tsx7
-rw-r--r--frontend/src/pages/History/Statistics/HistoryStats.module.scss9
-rw-r--r--frontend/src/pages/History/Statistics/HistoryStats.tsx (renamed from frontend/src/pages/History/Statistics/index.tsx)41
-rw-r--r--frontend/src/pages/History/history.test.tsx2
-rw-r--r--frontend/src/pages/Movies/Details/index.tsx8
-rw-r--r--frontend/src/pages/Movies/Details/table.tsx11
-rw-r--r--frontend/src/pages/Movies/index.tsx4
-rw-r--r--frontend/src/pages/Series/index.tsx19
-rw-r--r--frontend/src/pages/Settings/General/index.tsx17
-rw-r--r--frontend/src/pages/Settings/Languages/equals.tsx2
-rw-r--r--frontend/src/pages/Settings/Languages/table.tsx5
-rw-r--r--frontend/src/pages/Settings/Notifications/components.tsx2
-rw-r--r--frontend/src/pages/Settings/Providers/components.tsx30
-rw-r--r--frontend/src/pages/Settings/Radarr/index.tsx2
-rw-r--r--frontend/src/pages/Settings/Sonarr/index.tsx2
-rw-r--r--frontend/src/pages/Settings/Subtitles/index.tsx2
-rw-r--r--frontend/src/pages/Settings/components/Card.module.scss9
-rw-r--r--frontend/src/pages/Settings/components/Card.tsx33
-rw-r--r--frontend/src/pages/Settings/components/Layout.tsx2
-rw-r--r--frontend/src/pages/Settings/components/LayoutModal.tsx2
-rw-r--r--frontend/src/pages/Settings/components/Message.tsx2
-rw-r--r--frontend/src/pages/Settings/components/Section.test.tsx8
-rw-r--r--frontend/src/pages/Settings/components/Section.tsx2
-rw-r--r--frontend/src/pages/Settings/components/collapse.tsx2
-rw-r--r--frontend/src/pages/Settings/components/forms.test.tsx4
-rw-r--r--frontend/src/pages/Settings/components/forms.tsx5
-rw-r--r--frontend/src/pages/Settings/components/index.tsx4
-rw-r--r--frontend/src/pages/Settings/components/pathMapper.tsx2
-rw-r--r--frontend/src/pages/System/Announcements/table.tsx7
-rw-r--r--frontend/src/pages/System/Backups/table.tsx7
-rw-r--r--frontend/src/pages/System/Logs/index.tsx4
-rw-r--r--frontend/src/pages/System/Releases/index.tsx4
-rw-r--r--frontend/src/pages/System/Status/index.tsx9
-rw-r--r--frontend/src/pages/System/Status/table.tsx7
-rw-r--r--frontend/src/pages/System/Tasks/table.tsx7
-rw-r--r--frontend/src/pages/Wanted/Movies/index.tsx2
-rw-r--r--frontend/src/pages/Wanted/Series/index.tsx6
-rw-r--r--frontend/src/pages/errors/UIError.tsx6
-rw-r--r--frontend/src/pages/views/ItemOverview.tsx57
-rw-r--r--frontend/src/pages/views/MassEditor.tsx10
-rw-r--r--frontend/src/providers.tsx2
-rw-r--r--frontend/src/styles/index.ts1
-rw-r--r--frontend/src/styles/table.ts19
-rw-r--r--frontend/vite.config.ts10
106 files changed, 1187 insertions, 1241 deletions
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index bfc24c17f..f19cbab4d 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -9,12 +9,12 @@
"version": "1.0.0",
"license": "GPL-3",
"dependencies": {
- "@mantine/core": "^6.0.21",
- "@mantine/dropzone": "^6.0.21",
- "@mantine/form": "^6.0.21",
- "@mantine/hooks": "^6.0.21",
- "@mantine/modals": "^6.0.21",
- "@mantine/notifications": "^6.0.21",
+ "@mantine/core": "^7.10.1",
+ "@mantine/dropzone": "^7.10.1",
+ "@mantine/form": "^7.10.1",
+ "@mantine/hooks": "^7.10.1",
+ "@mantine/modals": "^7.10.1",
+ "@mantine/notifications": "^7.10.1",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -49,6 +49,8 @@
"husky": "^9.0.11",
"jsdom": "^24.0.0",
"lodash": "^4.17.21",
+ "postcss-preset-mantine": "^1.14.4",
+ "postcss-simple-vars": "^7.0.1",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"pretty-quick": "^4.0.0",
@@ -94,6 +96,7 @@
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
"integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+ "dev": true,
"dependencies": {
"@babel/highlight": "^7.24.2",
"picocolors": "^1.0.0"
@@ -335,6 +338,7 @@
"version": "7.24.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
"integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
+ "dev": true,
"dependencies": {
"@babel/types": "^7.24.0"
},
@@ -456,6 +460,7 @@
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
"integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
+ "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -464,6 +469,7 @@
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -509,6 +515,7 @@
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
"integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
+ "dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2",
@@ -523,6 +530,7 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
@@ -534,6 +542,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
@@ -547,6 +556,7 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
"dependencies": {
"color-name": "1.1.3"
}
@@ -554,12 +564,14 @@
"node_modules/@babel/highlight/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
},
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -568,6 +580,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
"engines": {
"node": ">=4"
}
@@ -576,6 +589,7 @@
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
@@ -2193,6 +2207,7 @@
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
"integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
+ "dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.23.4",
"@babel/helper-validator-identifier": "^7.22.20",
@@ -2208,120 +2223,6 @@
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
"dev": true
},
- "node_modules/@emotion/babel-plugin": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
- "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
- "peer": true,
- "dependencies": {
- "@babel/helper-module-imports": "^7.16.7",
- "@babel/runtime": "^7.18.3",
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/serialize": "^1.1.2",
- "babel-plugin-macros": "^3.1.0",
- "convert-source-map": "^1.5.0",
- "escape-string-regexp": "^4.0.0",
- "find-root": "^1.1.0",
- "source-map": "^0.5.7",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/cache": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
- "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
- "peer": true,
- "dependencies": {
- "@emotion/memoize": "^0.8.1",
- "@emotion/sheet": "^1.2.2",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/hash": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
- "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==",
- "peer": true
- },
- "node_modules/@emotion/memoize": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
- "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==",
- "peer": true
- },
- "node_modules/@emotion/react": {
- "version": "11.11.4",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
- "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
- "peer": true,
- "dependencies": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/cache": "^11.11.0",
- "@emotion/serialize": "^1.1.3",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "hoist-non-react-statics": "^3.3.1"
- },
- "peerDependencies": {
- "react": ">=16.8.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@emotion/serialize": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
- "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
- "peer": true,
- "dependencies": {
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/unitless": "^0.8.1",
- "@emotion/utils": "^1.2.1",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@emotion/sheet": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
- "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==",
- "peer": true
- },
- "node_modules/@emotion/unitless": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
- "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
- "peer": true
- },
- "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
- "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
- "peer": true,
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
- "node_modules/@emotion/utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
- "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==",
- "peer": true
- },
- "node_modules/@emotion/weak-memoize": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
- "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==",
- "peer": true
- },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
@@ -2791,13 +2692,13 @@
}
},
"node_modules/@floating-ui/react": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.2.tgz",
- "integrity": "sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w==",
+ "version": "0.26.12",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.12.tgz",
+ "integrity": "sha512-D09o62HrWdIkstF2kGekIKAC0/N/Dl6wo3CQsnLcOmO3LkW6Ik8uIb3kw8JYkwxNCcg+uJ2bpWUiIijTBep05w==",
"dependencies": {
- "@floating-ui/react-dom": "^1.3.0",
- "aria-hidden": "^1.1.3",
- "tabbable": "^6.0.1"
+ "@floating-ui/react-dom": "^2.0.0",
+ "@floating-ui/utils": "^0.2.0",
+ "tabbable": "^6.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
@@ -2805,11 +2706,11 @@
}
},
"node_modules/@floating-ui/react-dom": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz",
- "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==",
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
+ "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
"dependencies": {
- "@floating-ui/dom": "^1.2.1"
+ "@floating-ui/dom": "^1.6.1"
},
"peerDependencies": {
"react": ">=16.8.0",
@@ -3034,120 +2935,107 @@
}
},
"node_modules/@mantine/core": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/core/-/core-6.0.21.tgz",
- "integrity": "sha512-Kx4RrRfv0I+cOCIcsq/UA2aWcYLyXgW3aluAuW870OdXnbII6qg7RW28D+r9D76SHPxWFKwIKwmcucAG08Divg==",
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.10.1.tgz",
+ "integrity": "sha512-l9ypojKN3PjwO1CSLIsqxi7mA25+7w+xc71Q+JuCCREI0tuGwkZsKbIOpuTATIJOjPh8ycLiW7QxX1LYsRTq6w==",
+ "license": "MIT",
"dependencies": {
- "@floating-ui/react": "^0.19.1",
- "@mantine/styles": "6.0.21",
- "@mantine/utils": "6.0.21",
- "@radix-ui/react-scroll-area": "1.0.2",
- "react-remove-scroll": "^2.5.5",
- "react-textarea-autosize": "8.3.4"
+ "@floating-ui/react": "^0.26.9",
+ "clsx": "^2.1.1",
+ "react-number-format": "^5.3.1",
+ "react-remove-scroll": "^2.5.7",
+ "react-textarea-autosize": "8.5.3",
+ "type-fest": "^4.12.0"
},
"peerDependencies": {
- "@mantine/hooks": "6.0.21",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
+ "@mantine/hooks": "7.10.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ }
+ },
+ "node_modules/@mantine/core/node_modules/type-fest": {
+ "version": "4.15.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz",
+ "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@mantine/dropzone": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-6.0.21.tgz",
- "integrity": "sha512-v63tL4x7R1CvBNnxJVaVPhBVnQcfROQvyOV0xK/v0ZGNAzFxjJoiCRMGdlBjxnEawM0dRhNs/46ItpBgjQIr6g==",
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-7.10.1.tgz",
+ "integrity": "sha512-WzOLwMf8RJtakivPzSYBy3ZLuCtsZEbkJpAXNmM5PdsA8s9MKmY3jYA1MuZis1/NStAGm1d4twofH1KjGCMphg==",
+ "license": "MIT",
"dependencies": {
- "@mantine/utils": "6.0.21",
- "react-dropzone": "14.2.3"
+ "react-dropzone-esm": "15.0.1"
},
"peerDependencies": {
- "@mantine/core": "6.0.21",
- "@mantine/hooks": "6.0.21",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
+ "@mantine/core": "7.10.1",
+ "@mantine/hooks": "7.10.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
}
},
"node_modules/@mantine/form": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/form/-/form-6.0.21.tgz",
- "integrity": "sha512-d4tlxyZic7MSDnaPx/WliCX1sRFDkUd2nxx4MxxO2T4OSek0YDqTlSBCxeoveu60P+vrQQN5rbbsVsaOJBe4SQ==",
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/form/-/form-7.10.1.tgz",
+ "integrity": "sha512-mZwzg4GEWKEDKEIZu9FmSpGFzYYhFD2YArVOXUM0MMciUqX7yxSCon1PaPJxrV8ldc6FE+JLVI2+G2KVxJ3ZXA==",
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
- "klona": "^2.0.5"
+ "klona": "^2.0.6"
},
"peerDependencies": {
- "react": ">=16.8.0"
+ "react": "^18.2.0"
}
},
"node_modules/@mantine/hooks": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.21.tgz",
- "integrity": "sha512-sYwt5wai25W6VnqHbS5eamey30/HD5dNXaZuaVEAJ2i2bBv8C0cCiczygMDpAFiSYdXoSMRr/SZ2CrrPTzeNew==",
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.10.1.tgz",
+ "integrity": "sha512-0EH9WBWUdtQLGU3Ak+csQ77EtUxI6pPNfwZdRJQWcaA3f8SFOLo9h9CGxiikFExerhvuCeUlaTf3s+TB9Op/rw==",
+ "license": "MIT",
"peerDependencies": {
- "react": ">=16.8.0"
+ "react": "^18.2.0"
}
},
"node_modules/@mantine/modals": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-6.0.21.tgz",
- "integrity": "sha512-Gx2D/ZHMUuYF197JKMWey4K9FeGP9rxYp4lmAEXUrjXiST2fEhLZOdiD75KuOHXd1/sYAU9NcNRo9wXrlF/gUA==",
- "dependencies": {
- "@mantine/utils": "6.0.21"
- },
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-7.10.1.tgz",
+ "integrity": "sha512-2riQSNpVV7f0baizlqcggz9hx9/+y6SQTnW3zEkl/RIkuyK9dpeMFUG6M+M8ntwP79b7x9n7Em9PMWxRbgi28A==",
+ "license": "MIT",
"peerDependencies": {
- "@mantine/core": "6.0.21",
- "@mantine/hooks": "6.0.21",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
+ "@mantine/core": "7.10.1",
+ "@mantine/hooks": "7.10.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
}
},
"node_modules/@mantine/notifications": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-6.0.21.tgz",
- "integrity": "sha512-qsrqxuJHK8b67sf9Pfk+xyhvpf9jMsivW8vchfnJfjv7yz1lLvezjytMFp4fMDoYhjHnDPOEc/YFockK4muhOw==",
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-7.10.1.tgz",
+ "integrity": "sha512-cx3JR3BJzEzH6t2EF1ysrWVY/rdJk0WbSBQo/qFamJd2sbU+8XAHriI8Cx6hNo7uRGCwd8VGAj7Cf3aWK2VC5A==",
+ "license": "MIT",
"dependencies": {
- "@mantine/utils": "6.0.21",
- "react-transition-group": "4.4.2"
+ "@mantine/store": "7.10.1",
+ "react-transition-group": "4.4.5"
},
"peerDependencies": {
- "@mantine/core": "6.0.21",
- "@mantine/hooks": "6.0.21",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@mantine/styles": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-6.0.21.tgz",
- "integrity": "sha512-PVtL7XHUiD/B5/kZ/QvZOZZQQOj12QcRs3Q6nPoqaoPcOX5+S7bMZLMH0iLtcGq5OODYk0uxlvuJkOZGoPj8Mg==",
- "dependencies": {
- "clsx": "1.1.1",
- "csstype": "3.0.9"
- },
- "peerDependencies": {
- "@emotion/react": ">=11.9.0",
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@mantine/styles/node_modules/clsx": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
- "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
- "engines": {
- "node": ">=6"
+ "@mantine/core": "7.10.1",
+ "@mantine/hooks": "7.10.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
}
},
- "node_modules/@mantine/styles/node_modules/csstype": {
- "version": "3.0.9",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz",
- "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw=="
- },
- "node_modules/@mantine/utils": {
- "version": "6.0.21",
- "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-6.0.21.tgz",
- "integrity": "sha512-33RVDRop5jiWFao3HKd3Yp7A9mEq4HAJxJPTuYm1NkdqX6aTKOQK7wT8v8itVodBp+sb4cJK6ZVdD1UurK/txQ==",
+ "node_modules/@mantine/store": {
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@mantine/store/-/store-7.10.1.tgz",
+ "integrity": "sha512-KrGBsSoMsfrYeLxPwf5rFv0s2Nl/4wf+AaF/U1SpQrMgPI8vYokPXx52Wp3jCmlo12NCZnCIG+/6YHAdTWH1qQ==",
+ "license": "MIT",
"peerDependencies": {
- "react": ">=16.8.0"
+ "react": "^18.2.0"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
@@ -3222,137 +3110,6 @@
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
"dev": true
},
- "node_modules/@radix-ui/number": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.0.tgz",
- "integrity": "sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- }
- },
- "node_modules/@radix-ui/primitive": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz",
- "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- }
- },
- "node_modules/@radix-ui/react-compose-refs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz",
- "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-context": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz",
- "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-direction": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz",
- "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-presence": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz",
- "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==",
- "dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-compose-refs": "1.0.0",
- "@radix-ui/react-use-layout-effect": "1.0.0"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-primitive": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz",
- "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==",
- "dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-slot": "1.0.1"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-scroll-area": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.2.tgz",
- "integrity": "sha512-k8VseTxI26kcKJaX0HPwkvlNBPTs56JRdYzcZ/vzrNUkDlvXBy8sMc7WvCpYzZkHgb+hd72VW9MqkqecGtuNgg==",
- "dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/number": "1.0.0",
- "@radix-ui/primitive": "1.0.0",
- "@radix-ui/react-compose-refs": "1.0.0",
- "@radix-ui/react-context": "1.0.0",
- "@radix-ui/react-direction": "1.0.0",
- "@radix-ui/react-presence": "1.0.0",
- "@radix-ui/react-primitive": "1.0.1",
- "@radix-ui/react-use-callback-ref": "1.0.0",
- "@radix-ui/react-use-layout-effect": "1.0.0"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-slot": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz",
- "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==",
- "dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-compose-refs": "1.0.0"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-use-callback-ref": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz",
- "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
- "node_modules/@radix-ui/react-use-layout-effect": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz",
- "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
- "peerDependencies": {
- "react": "^16.8 || ^17.0 || ^18.0"
- }
- },
"node_modules/@remix-run/router": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
@@ -3899,7 +3656,8 @@
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "dev": true
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
@@ -4640,17 +4398,6 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
- "node_modules/aria-hidden": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
- "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
@@ -4848,14 +4595,6 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
- "node_modules/attr-accept": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
- "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -4903,6 +4642,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "dev": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
@@ -5107,10 +4847,20 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"engines": {
"node": ">=6"
}
},
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001607",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz",
@@ -5229,10 +4979,10 @@
}
},
"node_modules/clsx": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
- "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
- "dev": true,
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -5286,12 +5036,6 @@
"integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
"dev": true
},
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
- "peer": true
- },
"node_modules/core-js-compat": {
"version": "3.36.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz",
@@ -5309,6 +5053,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -5324,6 +5069,7 @@
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "dev": true,
"engines": {
"node": ">= 6"
}
@@ -5348,6 +5094,18 @@
"integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
"dev": true
},
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/cssstyle": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz",
@@ -5779,6 +5537,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@@ -5992,6 +5751,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -6606,17 +6366,6 @@
"node": "^10.12.0 || >=12.0.0"
}
},
- "node_modules/file-selector": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
- "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
- "dependencies": {
- "tslib": "^2.4.0"
- },
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -6629,12 +6378,6 @@
"node": ">=8"
}
},
- "node_modules/find-root": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
- "peer": true
- },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -6758,6 +6501,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -7035,6 +6779,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -7042,15 +6787,6 @@
"node": ">= 0.4"
}
},
- "node_modules/hoist-non-react-statics": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "peer": true,
- "dependencies": {
- "react-is": "^16.7.0"
- }
- },
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
@@ -7150,6 +6886,7 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -7243,7 +6980,8 @@
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
},
"node_modules/is-async-function": {
"version": "2.0.0",
@@ -7316,6 +7054,7 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
@@ -7934,7 +7673,8 @@
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
@@ -8053,7 +7793,8 @@
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
},
"node_modules/local-pkg": {
"version": "0.5.0",
@@ -8621,6 +8362,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -8632,6 +8374,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -8686,12 +8429,14 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -8714,7 +8459,8 @@
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -8776,6 +8522,108 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dev": true,
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-mixins": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-9.0.4.tgz",
+ "integrity": "sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==",
+ "dev": true,
+ "dependencies": {
+ "fast-glob": "^3.2.11",
+ "postcss-js": "^4.0.0",
+ "postcss-simple-vars": "^7.0.0",
+ "sugarss": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
+ "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-preset-mantine": {
+ "version": "1.14.4",
+ "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.14.4.tgz",
+ "integrity": "sha512-T1K3MVhU1hA9mJWfqoGvMcK5WKcHpVi4JUX6AYTbESvp78WneB/KFONUi+eXDG9Lpw62W/KNxEYl1ic3Dpm88w==",
+ "dev": true,
+ "dependencies": {
+ "postcss-mixins": "^9.0.4",
+ "postcss-nested": "^6.0.1"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.0"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-simple-vars": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz",
+ "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.1"
+ }
+ },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -8967,13 +8815,11 @@
"react": "^18.2.0"
}
},
- "node_modules/react-dropzone": {
- "version": "14.2.3",
- "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz",
- "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==",
+ "node_modules/react-dropzone-esm": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/react-dropzone-esm/-/react-dropzone-esm-15.0.1.tgz",
+ "integrity": "sha512-RdeGpqwHnoV/IlDFpQji7t7pTtlC2O1i/Br0LWkRZ9hYtLyce814S71h5NolnCZXsIN5wrZId6+8eQj2EBnEzg==",
"dependencies": {
- "attr-accept": "^2.2.2",
- "file-selector": "^0.6.0",
"prop-types": "^15.8.1"
},
"engines": {
@@ -8988,6 +8834,18 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/react-number-format": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.4.tgz",
+ "integrity": "sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-query": {
"version": "3.39.3",
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz",
@@ -9112,22 +8970,6 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
- "node_modules/react-smooth/node_modules/react-transition-group": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
- "dev": true,
- "dependencies": {
- "@babel/runtime": "^7.5.5",
- "dom-helpers": "^5.0.1",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.6.2"
- },
- "peerDependencies": {
- "react": ">=16.6.0",
- "react-dom": ">=16.6.0"
- }
- },
"node_modules/react-style-singleton": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
@@ -9164,11 +9006,11 @@
}
},
"node_modules/react-textarea-autosize": {
- "version": "8.3.4",
- "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz",
- "integrity": "sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==",
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz",
+ "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==",
"dependencies": {
- "@babel/runtime": "^7.10.2",
+ "@babel/runtime": "^7.20.13",
"use-composed-ref": "^1.3.0",
"use-latest": "^1.2.1"
},
@@ -9180,9 +9022,9 @@
}
},
"node_modules/react-transition-group": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
- "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
@@ -9375,6 +9217,7 @@
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -9391,6 +9234,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
"engines": {
"node": ">=4"
}
@@ -9701,15 +9545,6 @@
"node": ">=10.0.0"
}
},
- "node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
@@ -9905,11 +9740,21 @@
"integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
"dev": true
},
- "node_modules/stylis": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
- "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
- "peer": true
+ "node_modules/sugarss": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz",
+ "integrity": "sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3.3"
+ }
},
"node_modules/supports-color": {
"version": "7.2.0",
@@ -9927,6 +9772,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -9999,6 +9845,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
"engines": {
"node": ">=4"
}
@@ -10432,6 +10279,12 @@
}
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
"node_modules/v8-to-istanbul": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index eadb26496..971065e4d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -13,12 +13,12 @@
},
"private": true,
"dependencies": {
- "@mantine/core": "^6.0.21",
- "@mantine/dropzone": "^6.0.21",
- "@mantine/form": "^6.0.21",
- "@mantine/hooks": "^6.0.21",
- "@mantine/modals": "^6.0.21",
- "@mantine/notifications": "^6.0.21",
+ "@mantine/core": "^7.10.1",
+ "@mantine/dropzone": "^7.10.1",
+ "@mantine/form": "^7.10.1",
+ "@mantine/hooks": "^7.10.1",
+ "@mantine/modals": "^7.10.1",
+ "@mantine/notifications": "^7.10.1",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -53,6 +53,8 @@
"husky": "^9.0.11",
"jsdom": "^24.0.0",
"lodash": "^4.17.21",
+ "postcss-preset-mantine": "^1.14.4",
+ "postcss-simple-vars": "^7.0.1",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"pretty-quick": "^4.0.0",
diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs
new file mode 100644
index 000000000..e817f567b
--- /dev/null
+++ b/frontend/postcss.config.cjs
@@ -0,0 +1,14 @@
+module.exports = {
+ plugins: {
+ "postcss-preset-mantine": {},
+ "postcss-simple-vars": {
+ variables: {
+ "mantine-breakpoint-xs": "36em",
+ "mantine-breakpoint-sm": "48em",
+ "mantine-breakpoint-md": "62em",
+ "mantine-breakpoint-lg": "75em",
+ "mantine-breakpoint-xl": "88em",
+ },
+ },
+ },
+};
diff --git a/frontend/src/App/Header.module.scss b/frontend/src/App/Header.module.scss
new file mode 100644
index 000000000..85b3661a9
--- /dev/null
+++ b/frontend/src/App/Header.module.scss
@@ -0,0 +1,9 @@
+.header {
+ @include light {
+ color: var(--mantine-color-gray-0);
+ }
+
+ @include dark {
+ color: var(--mantine-color-dark-0);
+ }
+}
diff --git a/frontend/src/App/Header.tsx b/frontend/src/App/Header.tsx
index c15071045..6bb47f5b1 100644
--- a/frontend/src/App/Header.tsx
+++ b/frontend/src/App/Header.tsx
@@ -1,6 +1,5 @@
import { useSystem, useSystemSettings } from "@/apis/hooks";
import { Action, Search } from "@/components";
-import { Layout } from "@/constants";
import { useNavbar } from "@/contexts/Navbar";
import { useIsOnline } from "@/contexts/Online";
import { Environment, useGotoHomepage } from "@/utilities";
@@ -12,27 +11,16 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Anchor,
+ AppShell,
Avatar,
Badge,
Burger,
Divider,
Group,
- Header,
- MediaQuery,
Menu,
- createStyles,
} from "@mantine/core";
import { FunctionComponent } from "react";
-
-const useStyles = createStyles((theme) => {
- const headerBackgroundColor =
- theme.colorScheme === "light" ? theme.colors.gray[0] : theme.colors.dark[4];
- return {
- header: {
- backgroundColor: headerBackgroundColor,
- },
- };
-});
+import styles from "./Header.module.scss";
const AppHeader: FunctionComponent = () => {
const { data: settings } = useSystemSettings();
@@ -47,39 +35,28 @@ const AppHeader: FunctionComponent = () => {
const goHome = useGotoHomepage();
- const { classes } = useStyles();
-
return (
- <Header p="md" height={Layout.HEADER_HEIGHT} className={classes.header}>
- <Group position="apart" noWrap>
- <Group noWrap>
- <MediaQuery
- smallerThan={Layout.MOBILE_BREAKPOINT}
- styles={{ display: "none" }}
- >
- <Anchor onClick={goHome}>
- <Avatar
- alt="brand"
- size={32}
- src={`${Environment.baseUrl}/images/logo64.png`}
- ></Avatar>
- </Anchor>
- </MediaQuery>
- <MediaQuery
- largerThan={Layout.MOBILE_BREAKPOINT}
- styles={{ display: "none" }}
- >
- <Burger
- opened={showed}
- onClick={() => show(!showed)}
- size="sm"
- ></Burger>
- </MediaQuery>
+ <AppShell.Header p="md" className={styles.header}>
+ <Group justify="space-between" wrap="nowrap">
+ <Group wrap="nowrap">
+ <Anchor onClick={goHome} visibleFrom="sm">
+ <Avatar
+ alt="brand"
+ size={32}
+ src={`${Environment.baseUrl}/images/logo64.png`}
+ ></Avatar>
+ </Anchor>
+ <Burger
+ opened={showed}
+ onClick={() => show(!showed)}
+ size="sm"
+ hiddenFrom="sm"
+ ></Burger>
<Badge size="lg" radius="sm">
Bazarr
</Badge>
</Group>
- <Group spacing="xs" position="right" noWrap>
+ <Group gap="xs" justify="right" wrap="nowrap">
<Search></Search>
<Menu>
<Menu.Target>
@@ -95,13 +72,13 @@ const AppHeader: FunctionComponent = () => {
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
- icon={<FontAwesomeIcon icon={faArrowRotateLeft} />}
+ leftSection={<FontAwesomeIcon icon={faArrowRotateLeft} />}
onClick={() => restart()}
>
Restart
</Menu.Item>
<Menu.Item
- icon={<FontAwesomeIcon icon={faPowerOff} />}
+ leftSection={<FontAwesomeIcon icon={faPowerOff} />}
onClick={() => shutdown()}
>
Shutdown
@@ -114,7 +91,7 @@ const AppHeader: FunctionComponent = () => {
</Menu>
</Group>
</Group>
- </Header>
+ </AppShell.Header>
);
};
diff --git a/frontend/src/App/Navbar.module.scss b/frontend/src/App/Navbar.module.scss
new file mode 100644
index 000000000..ddb444e4d
--- /dev/null
+++ b/frontend/src/App/Navbar.module.scss
@@ -0,0 +1,56 @@
+.anchor {
+ border-color: var(--mantine-color-gray-5);
+ text-decoration: none;
+
+ @include dark {
+ border-color: var(--mantine-color-dark-5);
+ }
+
+ &.active {
+ border-left: 2px solid $color-brand-4;
+ background-color: var(--mantine-color-gray-1);
+
+ @include dark {
+ border-left: 2px solid $color-brand-8;
+ background-color: var(--mantine-color-dark-8);
+ }
+ }
+
+ &.hover {
+ background-color: var(--mantine-color-gray-0);
+
+ @include dark {
+ background-color: var(--mantine-color-dark-7);
+ }
+ }
+}
+
+.badge {
+ margin-left: auto;
+ text-decoration: none;
+ box-shadow: var(--mantine-shadow-xs);
+}
+
+.icon {
+ width: 1.4rem;
+ margin-right: var(--mantine-spacing-xs);
+}
+
+.nav {
+ background-color: var(--mantine-color-gray-2);
+
+ @include dark {
+ background-color: var(--mantine-color-dark-8);
+ }
+}
+
+.text {
+ display: inline-flex;
+ align-items: center;
+ width: 100%;
+ color: var(--mantine-color-gray-8);
+
+ @include dark {
+ color: var(--mantine-color-gray-5);
+ }
+}
diff --git a/frontend/src/App/Navbar.tsx b/frontend/src/App/Navbar.tsx
index c626dc257..8254f2fc0 100644
--- a/frontend/src/App/Navbar.tsx
+++ b/frontend/src/App/Navbar.tsx
@@ -1,5 +1,4 @@
import { Action } from "@/components";
-import { Layout } from "@/constants";
import { useNavbar } from "@/contexts/Navbar";
import { useRouteItems } from "@/Router";
import { CustomRouteObject, Route } from "@/Router/type";
@@ -14,19 +13,19 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Anchor,
+ AppShell,
Badge,
Collapse,
- createStyles,
Divider,
Group,
- Navbar as MantineNavbar,
Stack,
Text,
+ useComputedColorScheme,
useMantineColorScheme,
} from "@mantine/core";
import { useHover } from "@mantine/hooks";
import clsx from "clsx";
-import {
+import React, {
createContext,
FunctionComponent,
useContext,
@@ -35,6 +34,7 @@ import {
useState,
} from "react";
import { matchPath, NavLink, RouteObject, useLocation } from "react-router-dom";
+import styles from "./Navbar.module.scss";
const Selection = createContext<{
selection: string | null;
@@ -97,11 +97,12 @@ function useIsActive(parent: string, route: RouteObject) {
}
const AppNavbar: FunctionComponent = () => {
- const { showed } = useNavbar();
const [selection, select] = useState<string | null>(null);
- const { colorScheme, toggleColorScheme } = useMantineColorScheme();
- const dark = colorScheme === "dark";
+ const { toggleColorScheme } = useMantineColorScheme();
+ const computedColorScheme = useComputedColorScheme("light");
+
+ const dark = computedColorScheme === "dark";
const routes = useRouteItems();
@@ -111,23 +112,10 @@ const AppNavbar: FunctionComponent = () => {
}, [pathname]);
return (
- <MantineNavbar
- p="xs"
- hiddenBreakpoint={Layout.MOBILE_BREAKPOINT}
- hidden={!showed}
- width={{ [Layout.MOBILE_BREAKPOINT]: Layout.NAVBAR_WIDTH }}
- styles={(theme) => ({
- root: {
- backgroundColor:
- theme.colorScheme === "light"
- ? theme.colors.gray[2]
- : theme.colors.dark[6],
- },
- })}
- >
+ <AppShell.Navbar p="xs" className={styles.nav}>
<Selection.Provider value={{ selection, select }}>
- <MantineNavbar.Section grow>
- <Stack spacing={0}>
+ <AppShell.Section grow>
+ <Stack gap={0}>
{routes.map((route, idx) => (
<RouteItem
key={BuildKey("nav", idx)}
@@ -136,10 +124,10 @@ const AppNavbar: FunctionComponent = () => {
></RouteItem>
))}
</Stack>
- </MantineNavbar.Section>
+ </AppShell.Section>
<Divider></Divider>
- <MantineNavbar.Section mt="xs">
- <Group spacing="xs">
+ <AppShell.Section mt="xs">
+ <Group gap="xs">
<Action
label="Change Theme"
color={dark ? "yellow" : "indigo"}
@@ -159,9 +147,9 @@ const AppNavbar: FunctionComponent = () => {
></Action>
</Anchor>
</Group>
- </MantineNavbar.Section>
+ </AppShell.Section>
</Selection.Provider>
- </MantineNavbar>
+ </AppShell.Navbar>
);
};
@@ -186,7 +174,7 @@ const RouteItem: FunctionComponent<{
if (children !== undefined) {
const elements = (
- <Stack spacing={0}>
+ <Stack gap={0}>
{children.map((child, idx) => (
<RouteItem
parent={link}
@@ -199,7 +187,7 @@ const RouteItem: FunctionComponent<{
if (name) {
return (
- <Stack spacing={0}>
+ <Stack gap={0}>
<NavbarItem
primary
name={name}
@@ -244,53 +232,6 @@ const RouteItem: FunctionComponent<{
}
};
-const useStyles = createStyles((theme) => {
- const borderColor =
- theme.colorScheme === "light" ? theme.colors.gray[5] : theme.colors.dark[4];
-
- const activeBorderColor =
- theme.colorScheme === "light"
- ? theme.colors.brand[4]
- : theme.colors.brand[8];
-
- const activeBackgroundColor =
- theme.colorScheme === "light" ? theme.colors.gray[1] : theme.colors.dark[8];
-
- const hoverBackgroundColor =
- theme.colorScheme === "light" ? theme.colors.gray[0] : theme.colors.dark[7];
-
- const textColor =
- theme.colorScheme === "light" ? theme.colors.gray[8] : theme.colors.gray[5];
-
- return {
- text: {
- display: "inline-flex",
- alignItems: "center",
- width: "100%",
- color: textColor,
- },
- anchor: {
- textDecoration: "none",
- borderLeft: `2px solid ${borderColor}`,
- },
- active: {
- backgroundColor: activeBackgroundColor,
- borderLeft: `2px solid ${activeBorderColor}`,
- boxShadow: theme.shadows.xs,
- },
- hover: {
- backgroundColor: hoverBackgroundColor,
- },
- icon: { width: "1.4rem", marginRight: theme.spacing.xs },
- badge: {
- marginLeft: "auto",
- textDecoration: "none",
- boxShadow: theme.shadows.xs,
- color: textColor,
- },
- };
-});
-
interface NavbarItemProps {
name: string;
link: string;
@@ -308,8 +249,6 @@ const NavbarItem: FunctionComponent<NavbarItemProps> = ({
onClick,
primary = false,
}) => {
- const { classes } = useStyles();
-
const { show } = useNavbar();
const { ref, hovered } = useHover();
@@ -335,9 +274,9 @@ const NavbarItem: FunctionComponent<NavbarItemProps> = ({
}}
className={({ isActive }) =>
clsx(
- clsx(classes.anchor, {
- [classes.active]: isActive,
- [classes.hover]: hovered,
+ clsx(styles.anchor, {
+ [styles.active]: isActive,
+ [styles.hover]: hovered,
}),
)
}
@@ -347,18 +286,19 @@ const NavbarItem: FunctionComponent<NavbarItemProps> = ({
inline
p="xs"
size="sm"
- weight={primary ? "bold" : "normal"}
- className={classes.text}
+ fw={primary ? "bold" : "normal"}
+ className={styles.text}
+ span
>
{icon && (
<FontAwesomeIcon
- className={classes.icon}
+ className={styles.icon}
icon={icon}
></FontAwesomeIcon>
)}
{name}
- {shouldHideBadge === false && (
- <Badge className={classes.badge} radius="xs">
+ {!shouldHideBadge && (
+ <Badge className={styles.badge} radius="xs">
{badge}
</Badge>
)}
diff --git a/frontend/src/App/ThemeLoader.tsx b/frontend/src/App/ThemeLoader.tsx
new file mode 100644
index 000000000..2bc7e4005
--- /dev/null
+++ b/frontend/src/App/ThemeLoader.tsx
@@ -0,0 +1,39 @@
+import { useCallback, useEffect, useState } from "react";
+import { MantineColorScheme, useMantineColorScheme } from "@mantine/core";
+import { useSystemSettings } from "@/apis/hooks";
+
+const ThemeProvider = () => {
+ const [localScheme, setLocalScheme] = useState<MantineColorScheme | null>(
+ null,
+ );
+ const { setColorScheme } = useMantineColorScheme();
+
+ const settings = useSystemSettings();
+
+ const settingsColorScheme = settings.data?.general
+ .theme as MantineColorScheme;
+
+ const setScheme = useCallback(
+ (colorScheme: MantineColorScheme) => {
+ setColorScheme(colorScheme);
+ },
+ [setColorScheme],
+ );
+
+ useEffect(() => {
+ if (!settingsColorScheme) {
+ return;
+ }
+
+ if (localScheme === settingsColorScheme) {
+ return;
+ }
+
+ setScheme(settingsColorScheme);
+ setLocalScheme(settingsColorScheme);
+ }, [settingsColorScheme, setScheme, localScheme]);
+
+ return <></>;
+};
+
+export default ThemeProvider;
diff --git a/frontend/src/App/ThemeProvider.tsx b/frontend/src/App/ThemeProvider.tsx
new file mode 100644
index 000000000..d3a39cdb0
--- /dev/null
+++ b/frontend/src/App/ThemeProvider.tsx
@@ -0,0 +1,61 @@
+import {
+ ActionIcon,
+ AppShell,
+ Badge,
+ Button,
+ createTheme,
+ MantineProvider,
+} from "@mantine/core";
+import { FunctionComponent, PropsWithChildren } from "react";
+import ThemeLoader from "@/App/ThemeLoader";
+import "@mantine/core/styles.layer.css";
+import "@mantine/notifications/styles.layer.css";
+import styleVars from "@/assets/_variables.module.scss";
+import buttonClasses from "@/assets/button.module.scss";
+import actionIconClasses from "@/assets/action_icon.module.scss";
+import appShellClasses from "@/assets/app_shell.module.scss";
+import badgeClasses from "@/assets/badge.module.scss";
+
+const themeProvider = createTheme({
+ fontFamily: "Roboto, open sans, Helvetica Neue, Helvetica, Arial, sans-serif",
+ colors: {
+ brand: [
+ styleVars.colorBrand0,
+ styleVars.colorBrand1,
+ styleVars.colorBrand2,
+ styleVars.colorBrand3,
+ styleVars.colorBrand4,
+ styleVars.colorBrand5,
+ styleVars.colorBrand6,
+ styleVars.colorBrand7,
+ styleVars.colorBrand8,
+ styleVars.colorBrand9,
+ ],
+ },
+ primaryColor: "brand",
+ components: {
+ ActionIcon: ActionIcon.extend({
+ classNames: actionIconClasses,
+ }),
+ AppShell: AppShell.extend({
+ classNames: appShellClasses,
+ }),
+ Badge: Badge.extend({
+ classNames: badgeClasses,
+ }),
+ Button: Button.extend({
+ classNames: buttonClasses,
+ }),
+ },
+});
+
+const ThemeProvider: FunctionComponent<PropsWithChildren> = ({ children }) => {
+ return (
+ <MantineProvider theme={themeProvider} defaultColorScheme="auto">
+ <ThemeLoader />
+ {children}
+ </MantineProvider>
+ );
+};
+
+export default ThemeProvider;
diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx
index 4e09a97da..1b27734f4 100644
--- a/frontend/src/App/index.tsx
+++ b/frontend/src/App/index.tsx
@@ -1,7 +1,6 @@
import AppNavbar from "@/App/Navbar";
import { RouterNames } from "@/Router/RouterNames";
import ErrorBoundary from "@/components/ErrorBoundary";
-import { Layout } from "@/constants";
import NavbarProvider from "@/contexts/Navbar";
import OnlineProvider from "@/contexts/Online";
import { notification } from "@/modules/task";
@@ -13,6 +12,7 @@ import { showNotification } from "@mantine/notifications";
import { FunctionComponent, useEffect, useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import AppHeader from "./Header";
+import styleVars from "@/assets/_variables.module.scss";
const App: FunctionComponent = () => {
const navigate = useNavigate();
@@ -55,13 +55,19 @@ const App: FunctionComponent = () => {
<NavbarProvider value={{ showed: navbar, show: setNavbar }}>
<OnlineProvider value={{ online, setOnline }}>
<AppShell
- navbarOffsetBreakpoint={Layout.MOBILE_BREAKPOINT}
- header={<AppHeader></AppHeader>}
- navbar={<AppNavbar></AppNavbar>}
+ navbar={{
+ width: styleVars.navBarWidth,
+ breakpoint: "sm",
+ collapsed: { mobile: !navbar },
+ }}
+ header={{ height: { base: styleVars.headerHeight } }}
padding={0}
- fixed
>
- <Outlet></Outlet>
+ <AppHeader></AppHeader>
+ <AppNavbar></AppNavbar>
+ <AppShell.Main>
+ <Outlet></Outlet>
+ </AppShell.Main>
</AppShell>
</OnlineProvider>
</NavbarProvider>
diff --git a/frontend/src/App/theme.tsx b/frontend/src/App/theme.tsx
deleted file mode 100644
index 947b4f7a8..000000000
--- a/frontend/src/App/theme.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { useSystemSettings } from "@/apis/hooks";
-import {
- ColorScheme,
- ColorSchemeProvider,
- createEmotionCache,
- MantineProvider,
- MantineThemeOverride,
-} from "@mantine/core";
-import { useColorScheme } from "@mantine/hooks";
-import {
- FunctionComponent,
- PropsWithChildren,
- useCallback,
- useEffect,
- useState,
-} from "react";
-
-const theme: MantineThemeOverride = {
- fontFamily: "Roboto, open sans, Helvetica Neue, Helvetica, Arial, sans-serif",
- colors: {
- brand: [
- "#F8F0FC",
- "#F3D9FA",
- "#EEBEFA",
- "#E599F7",
- "#DA77F2",
- "#CC5DE8",
- "#BE4BDB",
- "#AE3EC9",
- "#9C36B5",
- "#862E9C",
- ],
- },
- primaryColor: "brand",
-};
-
-function useAutoColorScheme() {
- const settings = useSystemSettings();
- const settingsColorScheme = settings.data?.general.theme;
-
- let preferredColorScheme: ColorScheme = useColorScheme();
- switch (settingsColorScheme) {
- case "light":
- preferredColorScheme = "light" as ColorScheme;
- break;
- case "dark":
- preferredColorScheme = "dark" as ColorScheme;
- break;
- }
-
- const [colorScheme, setColorScheme] = useState(preferredColorScheme);
-
- // automatically switch dark/light theme
- useEffect(() => {
- setColorScheme(preferredColorScheme);
- }, [preferredColorScheme]);
-
- const toggleColorScheme = useCallback((value?: ColorScheme) => {
- setColorScheme((scheme) => value || (scheme === "dark" ? "light" : "dark"));
- }, []);
-
- return { colorScheme, setColorScheme, toggleColorScheme };
-}
-
-const emotionCache = createEmotionCache({ key: "bazarr" });
-
-const ThemeProvider: FunctionComponent<PropsWithChildren> = ({ children }) => {
- const { colorScheme, toggleColorScheme } = useAutoColorScheme();
-
- return (
- <ColorSchemeProvider
- colorScheme={colorScheme}
- toggleColorScheme={toggleColorScheme}
- >
- <MantineProvider
- withGlobalStyles
- withNormalizeCSS
- theme={{ colorScheme, ...theme }}
- emotionCache={emotionCache}
- >
- {children}
- </MantineProvider>
- </ColorSchemeProvider>
- );
-};
-
-export default ThemeProvider;
diff --git a/frontend/src/Router/index.tsx b/frontend/src/Router/index.tsx
index 335cf2d75..efed2edb2 100644
--- a/frontend/src/Router/index.tsx
+++ b/frontend/src/Router/index.tsx
@@ -53,7 +53,9 @@ import Redirector from "./Redirector";
import { RouterNames } from "./RouterNames";
import { CustomRouteObject } from "./type";
-const HistoryStats = lazy(() => import("@/pages/History/Statistics"));
+const HistoryStats = lazy(
+ () => import("@/pages/History/Statistics/HistoryStats"),
+);
const SystemStatusView = lazy(() => import("@/pages/System/Status"));
function useRoutes(): CustomRouteObject[] {
diff --git a/frontend/src/assets/_bazarr.scss b/frontend/src/assets/_bazarr.scss
new file mode 100644
index 000000000..6c23aac1a
--- /dev/null
+++ b/frontend/src/assets/_bazarr.scss
@@ -0,0 +1,40 @@
+$color-brand-0: #f8f0fc;
+$color-brand-1: #f3d9fa;
+$color-brand-2: #eebefa;
+$color-brand-3: #e599f7;
+$color-brand-4: #da77f2;
+$color-brand-5: #cc5de8;
+$color-brand-6: #be4bdb;
+$color-brand-7: #ae3ec9;
+$color-brand-8: #9c36b5;
+$color-brand-9: #862e9c;
+
+$header-height: 64px;
+
+:global {
+ .table-long-break {
+ overflow-wrap: anywhere;
+ }
+
+ .table-primary {
+ display: inline-block;
+
+ font-size: var(--mantine-font-size-sm);
+
+ @include smaller-than($mantine-breakpoint-sm) {
+ min-width: 12rem;
+ }
+ }
+
+ .table-no-wrap {
+ white-space: nowrap;
+ }
+
+ .table-select {
+ display: inline-block;
+
+ @include smaller-than($mantine-breakpoint-sm) {
+ min-width: 10rem;
+ }
+ }
+}
diff --git a/frontend/src/assets/_mantine.scss b/frontend/src/assets/_mantine.scss
new file mode 100644
index 000000000..93412c636
--- /dev/null
+++ b/frontend/src/assets/_mantine.scss
@@ -0,0 +1,61 @@
+@use "sass:math";
+
+$mantine-breakpoint-xs: "36em";
+$mantine-breakpoint-sm: "48em";
+$mantine-breakpoint-md: "62em";
+$mantine-breakpoint-lg: "75em";
+$mantine-breakpoint-xl: "88em";
+
+@function rem($value) {
+ @return #{math.div(math.div($value, $value * 0 + 1), 16)}rem;
+}
+
+@mixin light {
+ [data-mantine-color-scheme="light"] & {
+ @content;
+ }
+}
+
+@mixin dark {
+ [data-mantine-color-scheme="dark"] & {
+ @content;
+ }
+}
+
+@mixin hover {
+ @media (hover: hover) {
+ &:hover {
+ @content;
+ }
+ }
+
+ @media (hover: none) {
+ &:active {
+ @content;
+ }
+ }
+}
+
+@mixin smaller-than($breakpoint) {
+ @media (max-width: $breakpoint) {
+ @content;
+ }
+}
+
+@mixin larger-than($breakpoint) {
+ @media (min-width: $breakpoint) {
+ @content;
+ }
+}
+
+@mixin rtl {
+ [dir="rtl"] & {
+ @content;
+ }
+}
+
+@mixin ltr {
+ [dir="ltr"] & {
+ @content;
+ }
+}
diff --git a/frontend/src/assets/_variables.module.scss b/frontend/src/assets/_variables.module.scss
new file mode 100644
index 000000000..262d285b2
--- /dev/null
+++ b/frontend/src/assets/_variables.module.scss
@@ -0,0 +1,18 @@
+$navbar-width: 200;
+
+:export {
+ colorBrand0: $color-brand-0;
+ colorBrand1: $color-brand-1;
+ colorBrand2: $color-brand-2;
+ colorBrand3: $color-brand-3;
+ colorBrand4: $color-brand-4;
+ colorBrand5: $color-brand-5;
+ colorBrand6: $color-brand-6;
+ colorBrand7: $color-brand-7;
+ colorBrand8: $color-brand-8;
+ colorBrand9: $color-brand-9;
+
+ headerHeight: $header-height;
+
+ navBarWidth: $navbar-width;
+}
diff --git a/frontend/src/assets/action_icon.module.scss b/frontend/src/assets/action_icon.module.scss
new file mode 100644
index 000000000..c4bf2eefa
--- /dev/null
+++ b/frontend/src/assets/action_icon.module.scss
@@ -0,0 +1,14 @@
+@layer mantine {
+ .root {
+ &[data-variant="light"] {
+ color: var(--mantine-color-dark-0);
+ }
+
+ @include light {
+ &[data-variant="light"] {
+ background-color: var(--mantine-color-gray-1);
+ color: var(--mantine-color-dark-2);
+ }
+ }
+ }
+}
diff --git a/frontend/src/assets/app_shell.module.scss b/frontend/src/assets/app_shell.module.scss
new file mode 100644
index 000000000..b027c771a
--- /dev/null
+++ b/frontend/src/assets/app_shell.module.scss
@@ -0,0 +1,5 @@
+.main {
+ @include dark {
+ background-color: rgb(26, 27, 30);
+ }
+}
diff --git a/frontend/src/assets/badge.module.scss b/frontend/src/assets/badge.module.scss
new file mode 100644
index 000000000..830da2927
--- /dev/null
+++ b/frontend/src/assets/badge.module.scss
@@ -0,0 +1,8 @@
+.root {
+ background-color: var(--mantine-color-grape-light);
+
+ @include light {
+ color: var(--mantine-color-dark-filled);
+ background-color: var(--mantine-color-grape-light);
+ }
+}
diff --git a/frontend/src/assets/button.module.scss b/frontend/src/assets/button.module.scss
new file mode 100644
index 000000000..4ef306883
--- /dev/null
+++ b/frontend/src/assets/button.module.scss
@@ -0,0 +1,12 @@
+@layer mantine {
+ .root {
+ @include dark {
+ color: var(--mantine-color-dark-0);
+ }
+
+ &[data-variant="danger"] {
+ background-color: var(--mantine-color-red-9);
+ color: var(--mantine-color-red-0);
+ }
+ }
+}
diff --git a/frontend/src/components/Search.module.scss b/frontend/src/components/Search.module.scss
new file mode 100644
index 000000000..2c42098eb
--- /dev/null
+++ b/frontend/src/components/Search.module.scss
@@ -0,0 +1,9 @@
+.result {
+ @include light {
+ color: var(--mantine-color-dark-8);
+ }
+
+ @include dark {
+ color: var(--mantine-color-gray-1);
+ }
+}
diff --git a/frontend/src/components/Search.tsx b/frontend/src/components/Search.tsx
index bc4a9f8d3..75341e0a8 100644
--- a/frontend/src/components/Search.tsx
+++ b/frontend/src/components/Search.tsx
@@ -5,11 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Anchor,
Autocomplete,
- createStyles,
- SelectItemProps,
+ ComboboxItem,
+ OptionsFilter,
} from "@mantine/core";
-import { forwardRef, FunctionComponent, useMemo, useState } from "react";
+import { FunctionComponent, useMemo, useState } from "react";
import { Link } from "react-router-dom";
+import styles from "./Search.module.scss";
type SearchResultItem = {
value: string;
@@ -41,36 +42,35 @@ function useSearch(query: string) {
);
}
-const useStyles = createStyles((theme) => {
- return {
- result: {
- color:
- theme.colorScheme === "light"
- ? theme.colors.dark[8]
- : theme.colors.gray[1],
- },
- };
-});
-
-type ResultCompProps = SelectItemProps & SearchResultItem;
-
-const ResultComponent = forwardRef<HTMLDivElement, ResultCompProps>(
- ({ link, value }, ref) => {
- const styles = useStyles();
+const optionsFilter: OptionsFilter = ({ options, search }) => {
+ const lowercaseSearch = search.toLowerCase();
+ const trimmedSearch = search.trim();
+ return (options as ComboboxItem[]).filter((option) => {
return (
- <Anchor
- component={Link}
- to={link}
- underline={false}
- className={styles.classes.result}
- p="sm"
- >
- {value}
- </Anchor>
+ option.value.toLowerCase().includes(lowercaseSearch) ||
+ option.value
+ .normalize("NFD")
+ .replace(/[\u0300-\u036f]/g, "")
+ .toLowerCase()
+ .includes(trimmedSearch)
);
- },
-);
+ });
+};
+
+const ResultComponent = ({ name, link }: { name: string; link: string }) => {
+ return (
+ <Anchor
+ component={Link}
+ to={link}
+ underline="never"
+ className={styles.result}
+ p="sm"
+ >
+ {name}
+ </Anchor>
+ );
+};
const Search: FunctionComponent = () => {
const [query, setQuery] = useState("");
@@ -79,22 +79,22 @@ const Search: FunctionComponent = () => {
return (
<Autocomplete
- icon={<FontAwesomeIcon icon={faSearch} />}
- itemComponent={ResultComponent}
+ leftSection={<FontAwesomeIcon icon={faSearch} />}
+ renderOption={(input) => (
+ <ResultComponent
+ name={input.option.value}
+ link={
+ results.find((a) => a.value === input.option.value)?.link || "/"
+ }
+ />
+ )}
placeholder="Search"
size="sm"
data={results}
value={query}
onChange={setQuery}
onBlur={() => setQuery("")}
- filter={(value, item) =>
- item.value.toLowerCase().includes(value.toLowerCase().trim()) ||
- item.value
- .normalize("NFD")
- .replace(/[\u0300-\u036f]/g, "")
- .toLowerCase()
- .includes(value.trim())
- }
+ filter={optionsFilter}
></Autocomplete>
);
};
diff --git a/frontend/src/components/StateIcon.tsx b/frontend/src/components/StateIcon.tsx
index f9683f63a..73700a679 100644
--- a/frontend/src/components/StateIcon.tsx
+++ b/frontend/src/components/StateIcon.tsx
@@ -31,7 +31,7 @@ const StateIcon: FunctionComponent<StateIconProps> = ({
return <FontAwesomeIcon icon={faListCheck} />;
} else {
return (
- <Text color={hasIssues ? "yellow" : "green"}>
+ <Text c={hasIssues ? "yellow" : "green"} span>
<FontAwesomeIcon
icon={hasIssues ? faExclamationCircle : faCheckCircle}
/>
@@ -48,9 +48,9 @@ const StateIcon: FunctionComponent<StateIconProps> = ({
</Text>
</Popover.Target>
<Popover.Dropdown>
- <Group position="left" spacing="xl" noWrap grow>
- <Stack align="flex-start" justify="flex-start" spacing="xs" mb="auto">
- <Text color="green">
+ <Group justify="left" gap="xl" wrap="nowrap" grow>
+ <Stack align="flex-start" justify="flex-start" gap="xs" mb="auto">
+ <Text c="green">
<FontAwesomeIcon icon={faCheck}></FontAwesomeIcon>
</Text>
<List>
@@ -59,8 +59,8 @@ const StateIcon: FunctionComponent<StateIconProps> = ({
))}
</List>
</Stack>
- <Stack align="flex-start" justify="flex-start" spacing="xs" mb="auto">
- <Text color="yellow">
+ <Stack align="flex-start" justify="flex-start" gap="xs" mb="auto">
+ <Text c="yellow">
<FontAwesomeIcon icon={faTimes}></FontAwesomeIcon>
</Text>
<List>
diff --git a/frontend/src/components/SubtitleToolsMenu.tsx b/frontend/src/components/SubtitleToolsMenu.tsx
index e36a1e9e1..bdadb5cb4 100644
--- a/frontend/src/components/SubtitleToolsMenu.tsx
+++ b/frontend/src/components/SubtitleToolsMenu.tsx
@@ -148,7 +148,7 @@ const SubtitleToolsMenu: FunctionComponent<Props> = ({
<Menu.Item
key={tool.key}
disabled={disabledTools}
- icon={<FontAwesomeIcon icon={tool.icon}></FontAwesomeIcon>}
+ leftSection={<FontAwesomeIcon icon={tool.icon}></FontAwesomeIcon>}
onClick={() => {
if (tool.modal) {
modals.openContextModal(tool.modal, { selections });
@@ -164,7 +164,7 @@ const SubtitleToolsMenu: FunctionComponent<Props> = ({
<Menu.Label>Actions</Menu.Label>
<Menu.Item
disabled={selections.length !== 0 || onAction === undefined}
- icon={<FontAwesomeIcon icon={faSearch}></FontAwesomeIcon>}
+ leftSection={<FontAwesomeIcon icon={faSearch}></FontAwesomeIcon>}
onClick={() => {
onAction?.("search");
}}
@@ -174,7 +174,7 @@ const SubtitleToolsMenu: FunctionComponent<Props> = ({
<Menu.Item
disabled={selections.length === 0 || onAction === undefined}
color="red"
- icon={<FontAwesomeIcon icon={faTrash}></FontAwesomeIcon>}
+ leftSection={<FontAwesomeIcon icon={faTrash}></FontAwesomeIcon>}
onClick={() => {
modals.openConfirmModal({
title: "The following subtitles will be deleted",
diff --git a/frontend/src/components/bazarr/AudioList.tsx b/frontend/src/components/bazarr/AudioList.tsx
index ac9cce743..b73f71331 100644
--- a/frontend/src/components/bazarr/AudioList.tsx
+++ b/frontend/src/components/bazarr/AudioList.tsx
@@ -13,7 +13,7 @@ const AudioList: FunctionComponent<AudioListProps> = ({
...group
}) => {
return (
- <Group spacing="xs" {...group}>
+ <Group gap="xs" {...group}>
{audios.map((audio, idx) => (
<Badge color="blue" key={BuildKey(idx, audio.code2)} {...badgeProps}>
{audio.name}
diff --git a/frontend/src/components/bazarr/Language.test.tsx b/frontend/src/components/bazarr/Language.test.tsx
index 9e0e0fab8..e4e3b42b8 100644
--- a/frontend/src/components/bazarr/Language.test.tsx
+++ b/frontend/src/components/bazarr/Language.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, screen } from "@/tests";
+import { render, screen } from "@/tests";
import { describe, it } from "vitest";
import { Language } from ".";
@@ -9,13 +9,13 @@ describe("Language text", () => {
};
it("should show short text", () => {
- rawRender(<Language.Text value={testLanguage}></Language.Text>);
+ render(<Language.Text value={testLanguage}></Language.Text>);
expect(screen.getByText(testLanguage.code2)).toBeDefined();
});
it("should show long text", () => {
- rawRender(<Language.Text value={testLanguage} long></Language.Text>);
+ render(<Language.Text value={testLanguage} long></Language.Text>);
expect(screen.getByText(testLanguage.name)).toBeDefined();
});
@@ -23,7 +23,7 @@ describe("Language text", () => {
const testLanguageWithHi: Language.Info = { ...testLanguage, hi: true };
it("should show short text with HI", () => {
- rawRender(<Language.Text value={testLanguageWithHi}></Language.Text>);
+ render(<Language.Text value={testLanguageWithHi}></Language.Text>);
const expectedText = `${testLanguageWithHi.code2}:HI`;
@@ -31,7 +31,7 @@ describe("Language text", () => {
});
it("should show long text with HI", () => {
- rawRender(<Language.Text value={testLanguageWithHi} long></Language.Text>);
+ render(<Language.Text value={testLanguageWithHi} long></Language.Text>);
const expectedText = `${testLanguageWithHi.name} HI`;
@@ -44,7 +44,7 @@ describe("Language text", () => {
};
it("should show short text with Forced", () => {
- rawRender(<Language.Text value={testLanguageWithForced}></Language.Text>);
+ render(<Language.Text value={testLanguageWithForced}></Language.Text>);
const expectedText = `${testLanguageWithHi.code2}:Forced`;
@@ -52,9 +52,7 @@ describe("Language text", () => {
});
it("should show long text with Forced", () => {
- rawRender(
- <Language.Text value={testLanguageWithForced} long></Language.Text>,
- );
+ render(<Language.Text value={testLanguageWithForced} long></Language.Text>);
const expectedText = `${testLanguageWithHi.name} Forced`;
@@ -75,7 +73,7 @@ describe("Language list", () => {
];
it("should show all languages", () => {
- rawRender(<Language.List value={elements}></Language.List>);
+ render(<Language.List value={elements}></Language.List>);
elements.forEach((value) => {
expect(screen.getByText(value.name)).toBeDefined();
diff --git a/frontend/src/components/bazarr/Language.tsx b/frontend/src/components/bazarr/Language.tsx
index e5627c82e..ba1a884e6 100644
--- a/frontend/src/components/bazarr/Language.tsx
+++ b/frontend/src/components/bazarr/Language.tsx
@@ -49,7 +49,7 @@ type LanguageListProps = {
const LanguageList: FunctionComponent<LanguageListProps> = ({ value }) => {
return (
- <Group spacing="xs">
+ <Group gap="xs">
{value.map((v) => (
<Badge key={BuildKey(v.code2, v.code2, v.hi)}>{v.name}</Badge>
))}
diff --git a/frontend/src/components/forms/FrameRateForm.tsx b/frontend/src/components/forms/FrameRateForm.tsx
index 7e7eca24c..aa1723183 100644
--- a/frontend/src/components/forms/FrameRateForm.tsx
+++ b/frontend/src/components/forms/FrameRateForm.tsx
@@ -55,15 +55,17 @@ const FrameRateForm: FunctionComponent<Props> = ({ selections, onSubmit }) => {
})}
>
<Stack>
- <Group spacing="xs" grow>
+ <Group gap="xs" grow>
<NumberInput
placeholder="From"
- precision={2}
+ decimalScale={2}
+ fixedDecimalScale
{...form.getInputProps("from")}
></NumberInput>
<NumberInput
placeholder="To"
- precision={2}
+ decimalScale={2}
+ fixedDecimalScale
{...form.getInputProps("to")}
></NumberInput>
</Group>
diff --git a/frontend/src/components/forms/ItemEditForm.tsx b/frontend/src/components/forms/ItemEditForm.tsx
index 9f3856d54..64f87fb36 100644
--- a/frontend/src/components/forms/ItemEditForm.tsx
+++ b/frontend/src/components/forms/ItemEditForm.tsx
@@ -80,7 +80,7 @@ const ItemEditForm: FunctionComponent<Props> = ({
label="Languages Profile"
></Selector>
<Divider></Divider>
- <Group position="right">
+ <Group justify="right">
<Button
disabled={isOverlayVisible}
onClick={() => {
diff --git a/frontend/src/components/forms/MovieUploadForm.module.scss b/frontend/src/components/forms/MovieUploadForm.module.scss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/frontend/src/components/forms/MovieUploadForm.module.scss
diff --git a/frontend/src/components/forms/MovieUploadForm.tsx b/frontend/src/components/forms/MovieUploadForm.tsx
index b51614770..1d6c64fe7 100644
--- a/frontend/src/components/forms/MovieUploadForm.tsx
+++ b/frontend/src/components/forms/MovieUploadForm.tsx
@@ -1,7 +1,6 @@
import { useMovieSubtitleModification } from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { TaskGroup, task } from "@/modules/task";
-import { useTableStyles } from "@/styles";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
@@ -19,7 +18,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Button,
Checkbox,
- createStyles,
Divider,
MantineColor,
Stack,
@@ -79,21 +77,12 @@ interface Props {
onComplete?: () => void;
}
-const useStyles = createStyles((theme) => {
- return {
- wrapper: {
- overflowWrap: "anywhere",
- },
- };
-});
-
const MovieUploadForm: FunctionComponent<Props> = ({
files,
movie,
onComplete,
}) => {
const modals = useModals();
- const { classes } = useStyles();
const profile = useLanguageProfileBy(movie.profileId);
@@ -187,7 +176,7 @@ const MovieUploadForm: FunctionComponent<Props> = ({
return (
<TextPopover text={value?.messages}>
- <Text color={color} inline>
+ <Text c={color} inline>
<FontAwesomeIcon icon={icon}></FontAwesomeIcon>
</Text>
</TextPopover>
@@ -199,9 +188,7 @@ const MovieUploadForm: FunctionComponent<Props> = ({
id: "filename",
accessor: "file",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
-
- return <Text className={classes.primary}>{value.name}</Text>;
+ return <Text className="table-primary">{value.name}</Text>;
},
},
{
@@ -236,11 +223,10 @@ const MovieUploadForm: FunctionComponent<Props> = ({
Header: "Language",
accessor: "language",
Cell: ({ row: { original, index }, value }) => {
- const { classes } = useTableStyles();
return (
<Selector
{...languageOptions}
- className={classes.select}
+ className="table-long-break"
value={value}
onChange={(item) => {
action.mutate(index, { ...original, language: item });
@@ -289,7 +275,7 @@ const MovieUploadForm: FunctionComponent<Props> = ({
modals.closeSelf();
})}
>
- <Stack className={classes.wrapper}>
+ <Stack className="table-long-break">
<SimpleTable columns={columns} data={form.values.files}></SimpleTable>
<Divider></Divider>
<Button type="submit">Upload</Button>
diff --git a/frontend/src/components/forms/ProfileEditForm.module.scss b/frontend/src/components/forms/ProfileEditForm.module.scss
new file mode 100644
index 000000000..d98b850ff
--- /dev/null
+++ b/frontend/src/components/forms/ProfileEditForm.module.scss
@@ -0,0 +1,5 @@
+.content {
+ @include smaller-than($mantine-breakpoint-md) {
+ padding: 0;
+ }
+}
diff --git a/frontend/src/components/forms/ProfileEditForm.tsx b/frontend/src/components/forms/ProfileEditForm.tsx
index eecacd73e..93f09d8b8 100644
--- a/frontend/src/components/forms/ProfileEditForm.tsx
+++ b/frontend/src/components/forms/ProfileEditForm.tsx
@@ -1,6 +1,5 @@
import { Action, Selector, SelectorOption, SimpleTable } from "@/components";
import { useModals, withModal } from "@/modules/modals";
-import { useTableStyles } from "@/styles";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import { LOG } from "@/utilities/console";
import FormUtils from "@/utilities/form";
@@ -19,6 +18,7 @@ import { useForm } from "@mantine/form";
import { FunctionComponent, useCallback, useMemo } from "react";
import { Column } from "react-table";
import ChipInput from "../inputs/ChipInput";
+import styles from "./ProfileEditForm.module.scss";
export const anyCutoff = 65535;
@@ -162,12 +162,10 @@ const ProfileEditForm: FunctionComponent<Props> = ({
[code],
);
- const { classes } = useTableStyles();
-
return (
<Selector
{...languageOptions}
- className={classes.select}
+ className="table-select"
value={language}
onChange={(value) => {
if (value) {
@@ -260,13 +258,7 @@ const ProfileEditForm: FunctionComponent<Props> = ({
multiple
chevronPosition="right"
defaultValue={["Languages"]}
- styles={(theme) => ({
- content: {
- [theme.fn.smallerThan("md")]: {
- padding: 0,
- },
- },
- })}
+ className={styles.content}
>
<Accordion.Item value="Languages">
<Stack>
@@ -275,7 +267,7 @@ const ProfileEditForm: FunctionComponent<Props> = ({
columns={columns}
data={form.values.items}
></SimpleTable>
- <Button fullWidth color="light" onClick={addItem}>
+ <Button fullWidth onClick={addItem}>
Add Language
</Button>
<Selector
diff --git a/frontend/src/components/forms/SeriesUploadForm.tsx b/frontend/src/components/forms/SeriesUploadForm.tsx
index 5ce9c821a..f0e8e0d31 100644
--- a/frontend/src/components/forms/SeriesUploadForm.tsx
+++ b/frontend/src/components/forms/SeriesUploadForm.tsx
@@ -5,7 +5,6 @@ import {
} from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
-import { useTableStyles } from "@/styles";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
@@ -23,7 +22,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
Button,
Checkbox,
- createStyles,
Divider,
MantineColor,
Stack,
@@ -86,21 +84,12 @@ interface Props {
onComplete?: VoidFunction;
}
-const useStyles = createStyles((theme) => {
- return {
- wrapper: {
- overflowWrap: "anywhere",
- },
- };
-});
-
const SeriesUploadForm: FunctionComponent<Props> = ({
series,
files,
onComplete,
}) => {
const modals = useModals();
- const { classes } = useStyles();
const episodes = useEpisodesBySeriesId(series.sonarrSeriesId);
const episodeOptions = useSelectorOptions(
episodes.data ?? [],
@@ -225,8 +214,7 @@ const SeriesUploadForm: FunctionComponent<Props> = ({
id: "filename",
accessor: "file",
Cell: ({ value: { name } }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.primary}>{name}</Text>;
+ return <Text className="table-primary">{name}</Text>;
},
},
{
@@ -283,11 +271,10 @@ const SeriesUploadForm: FunctionComponent<Props> = ({
),
accessor: "language",
Cell: ({ row: { original, index }, value }) => {
- const { classes } = useTableStyles();
return (
<Selector
{...languageOptions}
- className={classes.select}
+ className="table-select"
value={value}
onChange={(item) => {
action.mutate(index, { ...original, language: item });
@@ -301,12 +288,11 @@ const SeriesUploadForm: FunctionComponent<Props> = ({
Header: "Episode",
accessor: "episode",
Cell: ({ value, row }) => {
- const { classes } = useTableStyles();
return (
<Selector
{...episodeOptions}
searchable
- className={classes.select}
+ className="table-select"
value={value}
onChange={(item) => {
action.mutate(row.index, { ...row.original, episode: item });
@@ -368,7 +354,7 @@ const SeriesUploadForm: FunctionComponent<Props> = ({
modals.closeSelf();
})}
>
- <Stack className={classes.wrapper}>
+ <Stack className="table-long-break">
<SimpleTable columns={columns} data={form.values.files}></SimpleTable>
<Divider></Divider>
<Button type="submit">Upload</Button>
diff --git a/frontend/src/components/forms/SyncSubtitleForm.tsx b/frontend/src/components/forms/SyncSubtitleForm.tsx
index b5136fc85..c2e4ae9a5 100644
--- a/frontend/src/components/forms/SyncSubtitleForm.tsx
+++ b/frontend/src/components/forms/SyncSubtitleForm.tsx
@@ -14,10 +14,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Alert, Button, Checkbox, Divider, Stack, Text } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
-import { Selector, SelectorOption } from "../inputs";
+import { GroupedSelector, Selector } from "../inputs";
const TaskName = "Syncing Subtitle";
+interface SelectOptions {
+ group: string;
+ items: { value: string; label: string }[];
+}
+
function useReferencedSubtitles(
mediaType: "episode" | "movie",
mediaId: number,
@@ -37,15 +42,21 @@ function useReferencedSubtitles(
const mediaData = mediaType === "episode" ? episodeData : movieData;
- const subtitles: { group: string; value: string; label: string }[] = [];
+ const subtitles: SelectOptions[] = [];
if (!mediaData.data) {
return [];
} else {
if (mediaData.data.audio_tracks.length > 0) {
+ const embeddedAudioGroup: SelectOptions = {
+ group: "Embedded audio tracks",
+ items: [],
+ };
+
+ subtitles.push(embeddedAudioGroup);
+
mediaData.data.audio_tracks.forEach((item) => {
- subtitles.push({
- group: "Embedded audio tracks",
+ embeddedAudioGroup.items.push({
value: item.stream,
label: `${item.name || item.language} (${item.stream})`,
});
@@ -53,9 +64,15 @@ function useReferencedSubtitles(
}
if (mediaData.data.embedded_subtitles_tracks.length > 0) {
+ const embeddedSubtitlesTrackGroup: SelectOptions = {
+ group: "Embedded subtitles tracks",
+ items: [],
+ };
+
+ subtitles.push(embeddedSubtitlesTrackGroup);
+
mediaData.data.embedded_subtitles_tracks.forEach((item) => {
- subtitles.push({
- group: "Embedded subtitles tracks",
+ embeddedSubtitlesTrackGroup.items.push({
value: item.stream,
label: `${item.name || item.language} (${item.stream})`,
});
@@ -63,10 +80,16 @@ function useReferencedSubtitles(
}
if (mediaData.data.external_subtitles_tracks.length > 0) {
+ const externalSubtitlesFilesGroup: SelectOptions = {
+ group: "External Subtitles files",
+ items: [],
+ };
+
+ subtitles.push(externalSubtitlesFilesGroup);
+
mediaData.data.external_subtitles_tracks.forEach((item) => {
if (item) {
- subtitles.push({
- group: "External Subtitles files",
+ externalSubtitlesFilesGroup.items.push({
value: item.path,
label: item.name,
});
@@ -105,7 +128,7 @@ const SyncSubtitleForm: FunctionComponent<Props> = ({
const mediaId = selections[0].id;
const subtitlesPath = selections[0].path;
- const subtitles: SelectorOption<string>[] = useReferencedSubtitles(
+ const subtitles: SelectOptions[] = useReferencedSubtitles(
mediaType,
mediaId,
subtitlesPath,
@@ -145,14 +168,14 @@ const SyncSubtitleForm: FunctionComponent<Props> = ({
>
<Text size="sm">{selections.length} subtitles selected</Text>
</Alert>
- <Selector
+ <GroupedSelector
clearable
disabled={subtitles.length === 0 || selections.length !== 1}
label="Reference"
placeholder="Default: choose automatically within video file"
options={subtitles}
{...form.getInputProps("reference")}
- ></Selector>
+ ></GroupedSelector>
<Selector
clearable
label="Max Offset Seconds"
diff --git a/frontend/src/components/forms/TimeOffsetForm.tsx b/frontend/src/components/forms/TimeOffsetForm.tsx
index 2792d64d8..3802f3a3e 100644
--- a/frontend/src/components/forms/TimeOffsetForm.tsx
+++ b/frontend/src/components/forms/TimeOffsetForm.tsx
@@ -70,7 +70,7 @@ const TimeOffsetForm: FunctionComponent<Props> = ({ selections, onSubmit }) => {
})}
>
<Stack>
- <Group align="end" spacing="xs" noWrap>
+ <Group align="end" gap="xs" wrap="nowrap">
<Button
color="gray"
variant="filled"
diff --git a/frontend/src/components/index.tsx b/frontend/src/components/index.tsx
index c3d7b4763..5ea97cf04 100644
--- a/frontend/src/components/index.tsx
+++ b/frontend/src/components/index.tsx
@@ -1,4 +1,4 @@
export { default as Search } from "./Search";
export * from "./inputs";
export * from "./tables";
-export { default as Toolbox } from "./toolbox";
+export { default as Toolbox } from "./toolbox/Toolbox";
diff --git a/frontend/src/components/inputs/Action.test.tsx b/frontend/src/components/inputs/Action.test.tsx
index 189aca076..80d24825b 100644
--- a/frontend/src/components/inputs/Action.test.tsx
+++ b/frontend/src/components/inputs/Action.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, screen } from "@/tests";
+import { render, screen } from "@/tests";
import { faStickyNote } from "@fortawesome/free-regular-svg-icons";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
@@ -9,7 +9,7 @@ const testIcon = faStickyNote;
describe("Action button", () => {
it("should be a button", () => {
- rawRender(<Action icon={testIcon} label={testLabel}></Action>);
+ render(<Action icon={testIcon} label={testLabel}></Action>);
const element = screen.getByRole("button", { name: testLabel });
expect(element.getAttribute("type")).toEqual("button");
@@ -17,7 +17,7 @@ describe("Action button", () => {
});
it("should show icon", () => {
- rawRender(<Action icon={testIcon} label={testLabel}></Action>);
+ render(<Action icon={testIcon} label={testLabel}></Action>);
// TODO: use getBy...
const element = screen.getByRole("img", { hidden: true });
@@ -27,7 +27,7 @@ describe("Action button", () => {
it("should call on-click event when clicked", async () => {
const onClickFn = vitest.fn();
- rawRender(
+ render(
<Action icon={testIcon} label={testLabel} onClick={onClickFn}></Action>,
);
diff --git a/frontend/src/components/inputs/ChipInput.test.tsx b/frontend/src/components/inputs/ChipInput.test.tsx
index cb52ee30c..4aa29e8b7 100644
--- a/frontend/src/components/inputs/ChipInput.test.tsx
+++ b/frontend/src/components/inputs/ChipInput.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, screen } from "@/tests";
+import { render, screen } from "@/tests";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
import ChipInput from "./ChipInput";
@@ -8,7 +8,7 @@ describe("ChipInput", () => {
// TODO: Support default value
it.skip("should works with default value", () => {
- rawRender(<ChipInput defaultValue={existedValues}></ChipInput>);
+ render(<ChipInput defaultValue={existedValues}></ChipInput>);
existedValues.forEach((value) => {
expect(screen.getByText(value)).toBeDefined();
@@ -16,7 +16,7 @@ describe("ChipInput", () => {
});
it("should works with value", () => {
- rawRender(<ChipInput value={existedValues}></ChipInput>);
+ render(<ChipInput value={existedValues}></ChipInput>);
existedValues.forEach((value) => {
expect(screen.getByText(value)).toBeDefined();
@@ -29,9 +29,7 @@ describe("ChipInput", () => {
expect(values).toContain(typedValue);
});
- rawRender(
- <ChipInput value={existedValues} onChange={mockedFn}></ChipInput>,
- );
+ render(<ChipInput value={existedValues} onChange={mockedFn}></ChipInput>);
const element = screen.getByRole("searchbox");
diff --git a/frontend/src/components/inputs/ChipInput.tsx b/frontend/src/components/inputs/ChipInput.tsx
index 4308f7189..1fa57084c 100644
--- a/frontend/src/components/inputs/ChipInput.tsx
+++ b/frontend/src/components/inputs/ChipInput.tsx
@@ -1,35 +1,29 @@
-import { useSelectorOptions } from "@/utilities";
import { FunctionComponent } from "react";
-import { MultiSelector, MultiSelectorProps } from "./Selector";
+import { TagsInput } from "@mantine/core";
-export type ChipInputProps = Omit<
- MultiSelectorProps<string>,
- | "searchable"
- | "creatable"
- | "getCreateLabel"
- | "onCreate"
- | "options"
- | "getkey"
->;
-
-const ChipInput: FunctionComponent<ChipInputProps> = ({ ...props }) => {
- const { value, onChange } = props;
-
- const options = useSelectorOptions(value ?? [], (v) => v);
+export interface ChipInputProps {
+ defaultValue?: string[] | undefined;
+ value?: readonly string[] | null;
+ label?: string;
+ onChange?: (value: string[]) => void;
+}
+const ChipInput: FunctionComponent<ChipInputProps> = ({
+ defaultValue,
+ value,
+ label,
+ onChange,
+}: ChipInputProps) => {
+ // TODO: Replace with our own custom implementation instead of just using the
+ // built-in TagsInput. https://mantine.dev/combobox/?e=MultiSelectCreatable
return (
- <MultiSelector
- {...props}
- {...options}
- creatable
- searchable
- getCreateLabel={(query) => `Add "${query}"`}
- onCreate={(query) => {
- onChange?.([...(value ?? []), query]);
- return query;
- }}
- buildOption={(value) => value}
- ></MultiSelector>
+ <TagsInput
+ defaultValue={defaultValue}
+ label={label}
+ value={value ? value?.map((v) => v) : []}
+ onChange={onChange}
+ clearable
+ ></TagsInput>
);
};
diff --git a/frontend/src/components/inputs/DropContent.module.scss b/frontend/src/components/inputs/DropContent.module.scss
new file mode 100644
index 000000000..c6c0f848a
--- /dev/null
+++ b/frontend/src/components/inputs/DropContent.module.scss
@@ -0,0 +1,4 @@
+.container {
+ pointer-events: none;
+ min-height: 220px;
+}
diff --git a/frontend/src/components/inputs/DropContent.tsx b/frontend/src/components/inputs/DropContent.tsx
index 38556220d..015794ead 100644
--- a/frontend/src/components/inputs/DropContent.tsx
+++ b/frontend/src/components/inputs/DropContent.tsx
@@ -4,24 +4,14 @@ import {
faXmark,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Group, Stack, Text, createStyles } from "@mantine/core";
+import { Group, Stack, Text } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { FunctionComponent } from "react";
-
-const useStyle = createStyles((theme) => {
- return {
- container: {
- pointerEvents: "none",
- minHeight: 220,
- },
- };
-});
+import styles from "./DropContent.module.scss";
export const DropContent: FunctionComponent = () => {
- const { classes } = useStyle();
-
return (
- <Group position="center" spacing="xl" className={classes.container}>
+ <Group justify="center" gap="xl" className={styles.container}>
<Dropzone.Idle>
<FontAwesomeIcon icon={faFileCirclePlus} size="2x" />
</Dropzone.Idle>
@@ -31,9 +21,9 @@ export const DropContent: FunctionComponent = () => {
<Dropzone.Reject>
<FontAwesomeIcon icon={faXmark} size="2x" />
</Dropzone.Reject>
- <Stack spacing={0}>
+ <Stack gap={0}>
<Text size="lg">Upload Subtitles</Text>
- <Text color="dimmed" size="sm">
+ <Text c="dimmed" size="sm">
Attach as many files as you like, you will need to select file
metadata before uploading
</Text>
diff --git a/frontend/src/components/inputs/FileBrowser.tsx b/frontend/src/components/inputs/FileBrowser.tsx
index ce57a4938..e1a4b5c18 100644
--- a/frontend/src/components/inputs/FileBrowser.tsx
+++ b/frontend/src/components/inputs/FileBrowser.tsx
@@ -1,7 +1,12 @@
import { useFileSystem } from "@/apis/hooks";
import { faFolder } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Autocomplete, AutocompleteProps } from "@mantine/core";
+import {
+ Autocomplete,
+ AutocompleteProps,
+ ComboboxItem,
+ OptionsFilter,
+} from "@mantine/core";
import { FunctionComponent, useEffect, useMemo, useRef, useState } from "react";
// TODO: use fortawesome icons
@@ -75,24 +80,28 @@ export const FileBrowser: FunctionComponent<FileBrowserProps> = ({
const ref = useRef<HTMLInputElement>(null);
+ const optionsFilter: OptionsFilter = ({ options, search }) => {
+ return (options as ComboboxItem[]).filter((option) => {
+ if (search === backKey) {
+ return true;
+ }
+
+ return option.value.includes(search);
+ });
+ };
+
return (
<Autocomplete
{...props}
ref={ref}
- icon={<FontAwesomeIcon icon={faFolder}></FontAwesomeIcon>}
+ leftSection={<FontAwesomeIcon icon={faFolder}></FontAwesomeIcon>}
placeholder="Click to start"
data={data}
value={value}
// Temporary solution of infinite dropdown items, fix later
limit={NaN}
maxDropdownHeight={240}
- filter={(value, item) => {
- if (item.value === backKey) {
- return true;
- } else {
- return item.value.includes(value);
- }
- }}
+ filter={optionsFilter}
onChange={(val) => {
if (val !== backKey) {
setValue(val);
diff --git a/frontend/src/components/inputs/Selector.test.tsx b/frontend/src/components/inputs/Selector.test.tsx
index a7b6cfb85..0da13e5bf 100644
--- a/frontend/src/components/inputs/Selector.test.tsx
+++ b/frontend/src/components/inputs/Selector.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, screen } from "@/tests";
+import { render, screen } from "@/tests";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
import { Selector, SelectorOption } from "./Selector";
@@ -18,20 +18,17 @@ const testOptions: SelectorOption<string>[] = [
describe("Selector", () => {
describe("options", () => {
it("should work with the SelectorOption", () => {
- rawRender(
- <Selector name={selectorName} options={testOptions}></Selector>,
- );
+ render(<Selector name={selectorName} options={testOptions}></Selector>);
- // TODO: selectorName
- expect(screen.getByRole("searchbox")).toBeDefined();
+ testOptions.forEach((o) => {
+ expect(screen.getByText(o.label)).toBeDefined();
+ });
});
it("should display when clicked", async () => {
- rawRender(
- <Selector name={selectorName} options={testOptions}></Selector>,
- );
+ render(<Selector name={selectorName} options={testOptions}></Selector>);
- const element = screen.getByRole("searchbox");
+ const element = screen.getByTestId("input-selector");
await userEvent.click(element);
@@ -44,7 +41,7 @@ describe("Selector", () => {
it("shouldn't show default value", async () => {
const option = testOptions[0];
- rawRender(
+ render(
<Selector
name={selectorName}
options={testOptions}
@@ -57,7 +54,7 @@ describe("Selector", () => {
it("shouldn't show value", async () => {
const option = testOptions[0];
- rawRender(
+ render(
<Selector
name={selectorName}
options={testOptions}
@@ -75,7 +72,7 @@ describe("Selector", () => {
const mockedFn = vitest.fn((value: string | null) => {
expect(value).toEqual(clickedOption.value);
});
- rawRender(
+ render(
<Selector
name={selectorName}
options={testOptions}
@@ -83,13 +80,13 @@ describe("Selector", () => {
></Selector>,
);
- const element = screen.getByRole("searchbox");
+ const element = screen.getByTestId("input-selector");
await userEvent.click(element);
await userEvent.click(screen.getByText(clickedOption.label));
- expect(mockedFn).toBeCalled();
+ expect(mockedFn).toHaveBeenCalled();
});
});
@@ -115,7 +112,7 @@ describe("Selector", () => {
const mockedFn = vitest.fn((value: { name: string } | null) => {
expect(value).toEqual(clickedOption.value);
});
- rawRender(
+ render(
<Selector
name={selectorName}
options={objectOptions}
@@ -124,20 +121,20 @@ describe("Selector", () => {
></Selector>,
);
- const element = screen.getByRole("searchbox");
+ const element = screen.getByTestId("input-selector");
await userEvent.click(element);
await userEvent.click(screen.getByText(clickedOption.label));
- expect(mockedFn).toBeCalled();
+ expect(mockedFn).toHaveBeenCalled();
});
});
describe("placeholder", () => {
it("should show when no selection", () => {
const placeholder = "Empty Selection";
- rawRender(
+ render(
<Selector
name={selectorName}
options={testOptions}
diff --git a/frontend/src/components/inputs/Selector.tsx b/frontend/src/components/inputs/Selector.tsx
index 0af276fc4..6a25144f5 100644
--- a/frontend/src/components/inputs/Selector.tsx
+++ b/frontend/src/components/inputs/Selector.tsx
@@ -1,9 +1,10 @@
import { LOG } from "@/utilities/console";
import {
+ ComboboxItem,
+ ComboboxParsedItemGroup,
MultiSelect,
MultiSelectProps,
Select,
- SelectItem,
SelectProps,
} from "@mantine/core";
import { isNull, isUndefined } from "lodash";
@@ -14,10 +15,10 @@ export type SelectorOption<T> = Override<
value: T;
label: string;
},
- SelectItem
+ ComboboxItem
>;
-type SelectItemWithPayload<T> = SelectItem & {
+type SelectItemWithPayload<T> = ComboboxItem & {
payload: T;
};
@@ -34,6 +35,30 @@ function DefaultKeyBuilder<T>(value: T) {
}
}
+export type GroupedSelectorProps<T> = Override<
+ {
+ options: ComboboxParsedItemGroup[];
+ getkey?: (value: T) => string;
+ },
+ Omit<SelectProps, "data">
+>;
+
+export function GroupedSelector<T>({
+ value,
+ options,
+ getkey = DefaultKeyBuilder,
+ ...select
+}: GroupedSelectorProps<T>) {
+ return (
+ <Select
+ data-testid="input-selector"
+ comboboxProps={{ withinPortal: true }}
+ data={options}
+ {...select}
+ ></Select>
+ );
+}
+
export type SelectorProps<T> = Override<
{
value?: T | null;
@@ -84,7 +109,7 @@ export function Selector<T>({
}, [defaultValue, keyRef]);
const wrappedOnChange = useCallback(
- (value: string) => {
+ (value: string | null) => {
const payload = data.find((v) => v.value === value)?.payload ?? null;
onChange?.(payload);
},
@@ -93,7 +118,8 @@ export function Selector<T>({
return (
<Select
- withinPortal={true}
+ data-testid="input-selector"
+ comboboxProps={{ withinPortal: true }}
data={data}
defaultValue={wrappedDefaultValue}
value={wrappedValue}
@@ -144,6 +170,7 @@ export function MultiSelector<T>({
() => value && value.map(labelRef.current),
[value],
);
+
const wrappedDefaultValue = useMemo(
() => defaultValue && defaultValue.map(labelRef.current),
[defaultValue],
@@ -168,6 +195,7 @@ export function MultiSelector<T>({
return (
<MultiSelect
{...select}
+ hidePickedOptions
value={wrappedValue}
defaultValue={wrappedDefaultValue}
onChange={wrappedOnChange}
diff --git a/frontend/src/components/modals/ManualSearchModal.tsx b/frontend/src/components/modals/ManualSearchModal.tsx
index 24799130d..9a9de605d 100644
--- a/frontend/src/components/modals/ManualSearchModal.tsx
+++ b/frontend/src/components/modals/ManualSearchModal.tsx
@@ -1,6 +1,5 @@
import { withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
-import { useTableStyles } from "@/styles";
import { GetItemId } from "@/utilities";
import {
faCaretDown,
@@ -31,9 +30,7 @@ type SupportType = Item.Movie | Item.Episode;
interface Props<T extends SupportType> {
download: (item: T, result: SearchResultType) => Promise<void>;
- query: (
- id?: number,
- ) => UseQueryResult<SearchResultType[] | undefined, unknown>;
+ query: (id?: number) => UseQueryResult<SearchResultType[] | undefined>;
item: T;
}
@@ -50,7 +47,8 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
const search = useCallback(() => {
setSearchStarted(true);
- results.refetch();
+
+ void results.refetch();
}, [results]);
const columns = useMemo<Column<SearchResultType>[]>(
@@ -59,8 +57,7 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
Header: "Score",
accessor: "score",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}%</Text>;
+ return <Text className="table-no-wrap">{value}%</Text>;
},
},
{
@@ -84,13 +81,12 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
Header: "Provider",
accessor: "provider",
Cell: (row) => {
- const { classes } = useTableStyles();
const value = row.value;
const { url } = row.row.original;
if (url) {
return (
<Anchor
- className={classes.noWrap}
+ className="table-no-wrap"
href={url}
target="_blank"
rel="noopener noreferrer"
@@ -107,7 +103,6 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
Header: "Release",
accessor: "release_info",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
const [open, setOpen] = useState(false);
const items = useMemo(
@@ -116,12 +111,12 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
);
if (value.length === 0) {
- return <Text color="dimmed">Cannot get release info</Text>;
+ return <Text c="dimmed">Cannot get release info</Text>;
}
return (
- <Stack spacing={0} onClick={() => setOpen((o) => !o)}>
- <Text className={classes.primary}>
+ <Stack gap={0} onClick={() => setOpen((o) => !o)}>
+ <Text className="table-primary" span>
{value[0]}
{value.length > 1 && (
<FontAwesomeIcon
@@ -141,8 +136,7 @@ function ManualSearchView<T extends SupportType>(props: Props<T>) {
Header: "Uploader",
accessor: "uploader",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value ?? "-"}</Text>;
+ return <Text className="table-no-wrap">{value ?? "-"}</Text>;
},
},
{
diff --git a/frontend/src/components/tables/BaseTable.module.scss b/frontend/src/components/tables/BaseTable.module.scss
new file mode 100644
index 000000000..e1e1eff0b
--- /dev/null
+++ b/frontend/src/components/tables/BaseTable.module.scss
@@ -0,0 +1,9 @@
+.container {
+ display: block;
+ max-width: 100%;
+ overflow-x: auto;
+}
+
+.table {
+ border-collapse: collapse;
+}
diff --git a/frontend/src/components/tables/BaseTable.tsx b/frontend/src/components/tables/BaseTable.tsx
index 6ec49e61a..f31e9fff0 100644
--- a/frontend/src/components/tables/BaseTable.tsx
+++ b/frontend/src/components/tables/BaseTable.tsx
@@ -1,8 +1,9 @@
import { useIsLoading } from "@/contexts";
import { usePageSize } from "@/utilities/storage";
-import { Box, createStyles, Skeleton, Table, Text } from "@mantine/core";
+import { Box, Skeleton, Table, Text } from "@mantine/core";
import { ReactNode, useMemo } from "react";
import { HeaderGroup, Row, TableInstance } from "react-table";
+import styles from "./BaseTable.module.scss";
export type BaseTableProps<T extends object> = TableInstance<T> & {
tableStyles?: TableStyleProps<T>;
@@ -18,37 +19,23 @@ export interface TableStyleProps<T extends object> {
rowRenderer?: (row: Row<T>) => Nullable<JSX.Element>;
}
-const useStyles = createStyles((theme) => {
- return {
- container: {
- display: "block",
- maxWidth: "100%",
- overflowX: "auto",
- },
- table: {
- borderCollapse: "collapse",
- },
- header: {},
- };
-});
-
function DefaultHeaderRenderer<T extends object>(
headers: HeaderGroup<T>[],
): JSX.Element[] {
return headers.map((col) => (
- <th style={{ whiteSpace: "nowrap" }} {...col.getHeaderProps()}>
+ <Table.Th style={{ whiteSpace: "nowrap" }} {...col.getHeaderProps()}>
{col.render("Header")}
- </th>
+ </Table.Th>
));
}
function DefaultRowRenderer<T extends object>(row: Row<T>): JSX.Element | null {
return (
- <tr {...row.getRowProps()}>
+ <Table.Tr {...row.getRowProps()}>
{row.cells.map((cell) => (
- <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
+ <Table.Td {...cell.getCellProps()}>{cell.render("Cell")}</Table.Td>
))}
- </tr>
+ </Table.Tr>
);
}
@@ -66,8 +53,6 @@ export default function BaseTable<T extends object>(props: BaseTableProps<T>) {
const headersRenderer = tableStyles?.headersRenderer ?? DefaultHeaderRenderer;
const rowRenderer = tableStyles?.rowRenderer ?? DefaultRowRenderer;
- const { classes } = useStyles();
-
const colCount = useMemo(() => {
return headerGroups.reduce(
(prev, curr) => (curr.headers.length > prev ? curr.headers.length : prev),
@@ -88,19 +73,19 @@ export default function BaseTable<T extends object>(props: BaseTableProps<T>) {
body = Array(tableStyles?.placeholder ?? pageSize)
.fill(0)
.map((_, i) => (
- <tr key={i}>
- <td colSpan={colCount}>
+ <Table.Tr key={i}>
+ <Table.Td colSpan={colCount}>
<Skeleton height={24}></Skeleton>
- </td>
- </tr>
+ </Table.Td>
+ </Table.Tr>
));
} else if (empty && tableStyles?.emptyText) {
body = (
- <tr>
- <td colSpan={colCount}>
- <Text align="center">{tableStyles.emptyText}</Text>
- </td>
- </tr>
+ <Table.Tr>
+ <Table.Td colSpan={colCount}>
+ <Text ta="center">{tableStyles.emptyText}</Text>
+ </Table.Td>
+ </Table.Tr>
);
} else {
body = rows.map((row) => {
@@ -110,20 +95,20 @@ export default function BaseTable<T extends object>(props: BaseTableProps<T>) {
}
return (
- <Box className={classes.container}>
+ <Box className={styles.container}>
<Table
- className={classes.table}
+ className={styles.table}
striped={tableStyles?.striped ?? true}
{...getTableProps()}
>
- <thead className={classes.header} hidden={tableStyles?.hideHeader}>
+ <Table.Thead hidden={tableStyles?.hideHeader}>
{headerGroups.map((headerGroup) => (
- <tr {...headerGroup.getHeaderGroupProps()}>
+ <Table.Tr {...headerGroup.getHeaderGroupProps()}>
{headersRenderer(headerGroup.headers)}
- </tr>
+ </Table.Tr>
))}
- </thead>
- <tbody {...getTableBodyProps()}>{body}</tbody>
+ </Table.Thead>
+ <Table.Tbody {...getTableBodyProps()}>{body}</Table.Tbody>
</Table>
</Box>
);
diff --git a/frontend/src/components/tables/GroupTable.tsx b/frontend/src/components/tables/GroupTable.tsx
index 3a8be3d1b..f58c868a5 100644
--- a/frontend/src/components/tables/GroupTable.tsx
+++ b/frontend/src/components/tables/GroupTable.tsx
@@ -1,6 +1,6 @@
import { faChevronCircleRight } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Box, Text } from "@mantine/core";
+import { Box, Text, Table } from "@mantine/core";
import {
Cell,
HeaderGroup,
@@ -29,8 +29,8 @@ function renderRow<T extends object>(row: Row<T>) {
if (cell) {
const rotation = row.isExpanded ? 90 : undefined;
return (
- <tr {...row.getRowProps()}>
- <td {...cell.getCellProps()} colSpan={row.cells.length}>
+ <Table.Tr {...row.getRowProps()}>
+ <Table.Td {...cell.getCellProps()} colSpan={row.cells.length}>
<Text {...row.getToggleRowExpandedProps()} p={2}>
{cell.render("Cell")}
<Box component="span" mx={12}>
@@ -40,21 +40,23 @@ function renderRow<T extends object>(row: Row<T>) {
></FontAwesomeIcon>
</Box>
</Text>
- </td>
- </tr>
+ </Table.Td>
+ </Table.Tr>
);
} else {
return null;
}
} else {
return (
- <tr {...row.getRowProps()}>
+ <Table.Tr {...row.getRowProps()}>
{row.cells
.filter((cell) => !cell.isPlaceholder)
.map((cell) => (
- <td {...cell.getCellProps()}>{renderCell(cell, row)}</td>
+ <Table.Td {...cell.getCellProps()}>
+ {renderCell(cell, row)}
+ </Table.Td>
))}
- </tr>
+ </Table.Tr>
);
}
}
@@ -64,7 +66,9 @@ function renderHeaders<T extends object>(
): JSX.Element[] {
return headers
.filter((col) => !col.isGrouped)
- .map((col) => <th {...col.getHeaderProps()}>{col.render("Header")}</th>);
+ .map((col) => (
+ <Table.Th {...col.getHeaderProps()}>{col.render("Header")}</Table.Th>
+ ));
}
type Props<T extends object> = Omit<
diff --git a/frontend/src/components/tables/PageControl.tsx b/frontend/src/components/tables/PageControl.tsx
index 0767593de..8cf528e31 100644
--- a/frontend/src/components/tables/PageControl.tsx
+++ b/frontend/src/components/tables/PageControl.tsx
@@ -28,7 +28,7 @@ const PageControl: FunctionComponent<Props> = ({
}, [total, goto]);
return (
- <Group p={16} position="apart">
+ <Group p={16} justify="apart">
<Text size="sm">
Show {start} to {end} of {total} entries
</Text>
diff --git a/frontend/src/components/toolbox/Button.tsx b/frontend/src/components/toolbox/Button.tsx
index 735ef3ca1..022cf9081 100644
--- a/frontend/src/components/toolbox/Button.tsx
+++ b/frontend/src/components/toolbox/Button.tsx
@@ -24,7 +24,7 @@ const ToolboxButton: FunctionComponent<ToolboxButtonProps> = ({
<Button
color="dark"
variant="subtle"
- leftIcon={<FontAwesomeIcon icon={icon}></FontAwesomeIcon>}
+ leftSection={<FontAwesomeIcon icon={icon}></FontAwesomeIcon>}
{...props}
>
<Text size="xs">{children}</Text>
diff --git a/frontend/src/components/toolbox/Toolbox.module.scss b/frontend/src/components/toolbox/Toolbox.module.scss
new file mode 100644
index 000000000..76d90ae47
--- /dev/null
+++ b/frontend/src/components/toolbox/Toolbox.module.scss
@@ -0,0 +1,9 @@
+.group {
+ @include light {
+ color: var(--mantine-color-gray-3);
+ }
+
+ @include dark {
+ color: var(--mantine-color-dark-5);
+ }
+}
diff --git a/frontend/src/components/toolbox/index.tsx b/frontend/src/components/toolbox/Toolbox.tsx
index 6995e111d..fbeadbc5f 100644
--- a/frontend/src/components/toolbox/index.tsx
+++ b/frontend/src/components/toolbox/Toolbox.tsx
@@ -1,15 +1,7 @@
-import { createStyles, Group } from "@mantine/core";
+import { Group } from "@mantine/core";
import { FunctionComponent, PropsWithChildren } from "react";
import ToolboxButton, { ToolboxMutateButton } from "./Button";
-
-const useStyles = createStyles((theme) => ({
- group: {
- backgroundColor:
- theme.colorScheme === "light"
- ? theme.colors.gray[3]
- : theme.colors.dark[5],
- },
-}));
+import styles from "./Toolbox.module.scss";
declare type ToolboxComp = FunctionComponent<PropsWithChildren> & {
Button: typeof ToolboxButton;
@@ -17,9 +9,8 @@ declare type ToolboxComp = FunctionComponent<PropsWithChildren> & {
};
const Toolbox: ToolboxComp = ({ children }) => {
- const { classes } = useStyles();
return (
- <Group p={12} position="apart" className={classes.group}>
+ <Group p={12} justify="apart" className={styles.group}>
{children}
</Group>
);
diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts
index 8defd1ad9..6320fa4f3 100644
--- a/frontend/src/constants.ts
+++ b/frontend/src/constants.ts
@@ -1,9 +1 @@
-import { MantineNumberSize } from "@mantine/core";
-
export const GithubRepoRoot = "https://github.com/morpheus65535/bazarr";
-
-export const Layout = {
- NAVBAR_WIDTH: 200,
- HEADER_HEIGHT: 64,
- MOBILE_BREAKPOINT: "sm" as MantineNumberSize,
-};
diff --git a/frontend/src/modules/socketio/reducer.ts b/frontend/src/modules/socketio/reducer.ts
index 403fc0ce0..35a553de9 100644
--- a/frontend/src/modules/socketio/reducer.ts
+++ b/frontend/src/modules/socketio/reducer.ts
@@ -27,7 +27,7 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
update: (msg) => {
msg
.map((message) => notification.info("Notification", message))
- .forEach(showNotification);
+ .forEach((data) => showNotification(data));
},
},
{
diff --git a/frontend/src/modules/task/index.ts b/frontend/src/modules/task/index.ts
index f4dc956cd..5a33a39a3 100644
--- a/frontend/src/modules/task/index.ts
+++ b/frontend/src/modules/task/index.ts
@@ -133,7 +133,7 @@ class TaskDispatcher {
public removeProgress(ids: string[]) {
setTimeout(
- () => ids.forEach(hideNotification),
+ () => ids.forEach((id) => hideNotification(id)),
notification.PROGRESS_TIMEOUT,
);
}
diff --git a/frontend/src/modules/task/notification.ts b/frontend/src/modules/task/notification.ts
index bb796b213..97601b452 100644
--- a/frontend/src/modules/task/notification.ts
+++ b/frontend/src/modules/task/notification.ts
@@ -1,7 +1,7 @@
-import { NotificationProps } from "@mantine/notifications";
+import { NotificationData } from "@mantine/notifications";
export const notification = {
- info: (title: string, message: string): NotificationProps => {
+ info: (title: string, message: string): NotificationData => {
return {
title,
message,
@@ -9,7 +9,7 @@ export const notification = {
};
},
- warn: (title: string, message: string): NotificationProps => {
+ warn: (title: string, message: string): NotificationData => {
return {
title,
message,
@@ -18,7 +18,7 @@ export const notification = {
};
},
- error: (title: string, message: string): NotificationProps => {
+ error: (title: string, message: string): NotificationData => {
return {
title,
message,
@@ -33,7 +33,7 @@ export const notification = {
pending: (
id: string,
header: string,
- ): NotificationProps & { id: string } => {
+ ): NotificationData & { id: string } => {
return {
id,
title: header,
@@ -48,7 +48,7 @@ export const notification = {
body: string,
current: number,
total: number,
- ): NotificationProps & { id: string } => {
+ ): NotificationData & { id: string } => {
return {
id,
title: header,
@@ -57,7 +57,7 @@ export const notification = {
autoClose: false,
};
},
- end: (id: string, header: string): NotificationProps & { id: string } => {
+ end: (id: string, header: string): NotificationData & { id: string } => {
return {
id,
title: header,
diff --git a/frontend/src/pages/Authentication.tsx b/frontend/src/pages/Authentication.tsx
index baf21f6cd..33fda6f01 100644
--- a/frontend/src/pages/Authentication.tsx
+++ b/frontend/src/pages/Authentication.tsx
@@ -52,7 +52,7 @@ const Authentication: FunctionComponent = () => {
{...form.getInputProps("password")}
></PasswordInput>
<Divider></Divider>
- <Button fullWidth uppercase type="submit">
+ <Button fullWidth tt="uppercase" type="submit">
Login
</Button>
</Stack>
diff --git a/frontend/src/pages/Blacklist/Movies/table.tsx b/frontend/src/pages/Blacklist/Movies/table.tsx
index 9ab06f2ba..6609d186d 100644
--- a/frontend/src/pages/Blacklist/Movies/table.tsx
+++ b/frontend/src/pages/Blacklist/Movies/table.tsx
@@ -3,7 +3,6 @@ import { PageTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
import Language from "@/components/bazarr/Language";
import TextPopover from "@/components/TextPopover";
-import { useTableStyles } from "@/styles";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
@@ -22,9 +21,8 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
accessor: "title",
Cell: (row) => {
const target = `/movies/${row.row.original.radarrId}`;
- const { classes } = useTableStyles();
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{row.value}
</Anchor>
);
diff --git a/frontend/src/pages/Blacklist/Series/table.tsx b/frontend/src/pages/Blacklist/Series/table.tsx
index a67069717..96ea1a8a7 100644
--- a/frontend/src/pages/Blacklist/Series/table.tsx
+++ b/frontend/src/pages/Blacklist/Series/table.tsx
@@ -3,7 +3,6 @@ import { PageTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
import Language from "@/components/bazarr/Language";
import TextPopover from "@/components/TextPopover";
-import { useTableStyles } from "@/styles";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
@@ -21,10 +20,9 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
Header: "Series",
accessor: "seriesTitle",
Cell: (row) => {
- const { classes } = useTableStyles();
const target = `/series/${row.row.original.sonarrSeriesId}`;
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{row.value}
</Anchor>
);
diff --git a/frontend/src/pages/Episodes/index.tsx b/frontend/src/pages/Episodes/index.tsx
index 28e375744..b016490ff 100644
--- a/frontend/src/pages/Episodes/index.tsx
+++ b/frontend/src/pages/Episodes/index.tsx
@@ -125,7 +125,7 @@ const SeriesEpisodesView: FunctionComponent = () => {
<DropContent></DropContent>
</Dropzone.FullScreen>
<Toolbox>
- <Group spacing="xs">
+ <Group gap="xs">
<Toolbox.Button
icon={faSync}
disabled={!available || hasTask}
@@ -160,7 +160,7 @@ const SeriesEpisodesView: FunctionComponent = () => {
Search
</Toolbox.Button>
</Group>
- <Group spacing="xs">
+ <Group gap="xs">
<Toolbox.Button
disabled={
series === undefined ||
diff --git a/frontend/src/pages/Episodes/table.tsx b/frontend/src/pages/Episodes/table.tsx
index 5a310c359..f1aa6a52e 100644
--- a/frontend/src/pages/Episodes/table.tsx
+++ b/frontend/src/pages/Episodes/table.tsx
@@ -6,7 +6,6 @@ import { AudioList } from "@/components/bazarr";
import { EpisodeHistoryModal } from "@/components/modals";
import { EpisodeSearchModal } from "@/components/modals/ManualSearchModal";
import { useModals } from "@/modules/modals";
-import { useTableStyles } from "@/styles";
import { BuildKey, filterSubtitleBy } from "@/utilities";
import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
@@ -92,7 +91,7 @@ const Table: FunctionComponent<Props> = ({
{
accessor: "season",
Cell: (row) => {
- return <Text>Season {row.value}</Text>;
+ return <Text span>Season {row.value}</Text>;
},
},
{
@@ -103,11 +102,9 @@ const Table: FunctionComponent<Props> = ({
Header: "Title",
accessor: "title",
Cell: ({ value, row }) => {
- const { classes } = useTableStyles();
-
return (
<TextPopover text={row.original.sceneName}>
- <Text className={classes.primary}>{value}</Text>
+ <Text className="table-primary">{value}</Text>
</TextPopover>
);
},
@@ -156,7 +153,7 @@ const Table: FunctionComponent<Props> = ({
}, [episode, seriesId]);
return (
- <Group spacing="xs" noWrap>
+ <Group gap="xs" wrap="nowrap">
{elements}
</Group>
);
@@ -168,7 +165,7 @@ const Table: FunctionComponent<Props> = ({
Cell: ({ row }) => {
const modals = useModals();
return (
- <Group spacing="xs" noWrap>
+ <Group gap="xs" wrap="nowrap">
<Action
label="Manual Search"
disabled={disabled}
diff --git a/frontend/src/pages/History/Movies/index.tsx b/frontend/src/pages/History/Movies/index.tsx
index ee4e98df0..bdbe5966a 100644
--- a/frontend/src/pages/History/Movies/index.tsx
+++ b/frontend/src/pages/History/Movies/index.tsx
@@ -6,7 +6,6 @@ import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import TextPopover from "@/components/TextPopover";
import HistoryView from "@/pages/views/HistoryView";
-import { useTableStyles } from "@/styles";
import {
faFileExcel,
faInfoCircle,
@@ -29,10 +28,9 @@ const MoviesHistoryView: FunctionComponent = () => {
Header: "Name",
accessor: "title",
Cell: ({ row, value }) => {
- const { classes } = useTableStyles();
const target = `/movies/${row.original.radarrId}`;
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{value}
</Anchor>
);
diff --git a/frontend/src/pages/History/Series/index.tsx b/frontend/src/pages/History/Series/index.tsx
index d6b1469bf..e2d2f9662 100644
--- a/frontend/src/pages/History/Series/index.tsx
+++ b/frontend/src/pages/History/Series/index.tsx
@@ -9,7 +9,6 @@ import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import TextPopover from "@/components/TextPopover";
import HistoryView from "@/pages/views/HistoryView";
-import { useTableStyles } from "@/styles";
import {
faFileExcel,
faInfoCircle,
@@ -32,11 +31,10 @@ const SeriesHistoryView: FunctionComponent = () => {
Header: "Series",
accessor: "seriesTitle",
Cell: (row) => {
- const { classes } = useTableStyles();
const target = `/series/${row.row.original.sonarrSeriesId}`;
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{row.value}
</Anchor>
);
@@ -50,8 +48,7 @@ const SeriesHistoryView: FunctionComponent = () => {
Header: "Title",
accessor: "episodeTitle",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}</Text>;
+ return <Text className="table-no-wrap">{value}</Text>;
},
},
{
diff --git a/frontend/src/pages/History/Statistics/HistoryStats.module.scss b/frontend/src/pages/History/Statistics/HistoryStats.module.scss
new file mode 100644
index 000000000..3c7c04e10
--- /dev/null
+++ b/frontend/src/pages/History/Statistics/HistoryStats.module.scss
@@ -0,0 +1,9 @@
+.container {
+ display: flex;
+ flex-direction: column;
+ height: calc(100vh - $header-height);
+}
+
+.chart {
+ height: 90%;
+}
diff --git a/frontend/src/pages/History/Statistics/index.tsx b/frontend/src/pages/History/Statistics/HistoryStats.tsx
index 243225538..9358cac30 100644
--- a/frontend/src/pages/History/Statistics/index.tsx
+++ b/frontend/src/pages/History/Statistics/HistoryStats.tsx
@@ -5,16 +5,8 @@ import {
} from "@/apis/hooks";
import { Selector, Toolbox } from "@/components";
import { QueryOverlay } from "@/components/async";
-import Language from "@/components/bazarr/Language";
-import { Layout } from "@/constants";
import { useSelectorOptions } from "@/utilities";
-import {
- Box,
- Container,
- SimpleGrid,
- createStyles,
- useMantineTheme,
-} from "@mantine/core";
+import { Box, Container, SimpleGrid, useMantineTheme } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { merge } from "lodash";
import { FunctionComponent, useMemo, useState } from "react";
@@ -29,17 +21,7 @@ import {
YAxis,
} from "recharts";
import { actionOptions, timeFrameOptions } from "./options";
-
-const useStyles = createStyles((theme) => ({
- container: {
- display: "flex",
- flexDirection: "column",
- height: `calc(100vh - ${Layout.HEADER_HEIGHT}px)`,
- },
- chart: {
- height: "90%",
- },
-}));
+import styles from "./HistoryStats.module.scss";
const HistoryStats: FunctionComponent = () => {
const { data: providers } = useSystemProviders(true);
@@ -71,8 +53,8 @@ const HistoryStats: FunctionComponent = () => {
date: v.date,
series: v.count,
}));
- const result = merge(movies, series);
- return result;
+
+ return merge(movies, series);
} else {
return [];
}
@@ -80,20 +62,13 @@ const HistoryStats: FunctionComponent = () => {
useDocumentTitle("History Statistics - Bazarr");
- const { classes } = useStyles();
const theme = useMantineTheme();
return (
- <Container fluid px={0} className={classes.container}>
+ <Container fluid px={0} className={styles.container}>
<QueryOverlay result={stats}>
<Toolbox>
- <SimpleGrid
- cols={4}
- breakpoints={[
- { maxWidth: "sm", cols: 4 },
- { maxWidth: "xs", cols: 2 },
- ]}
- >
+ <SimpleGrid cols={{ base: 4, xs: 2 }}>
<Selector
placeholder="Time..."
options={timeFrameOptions}
@@ -123,9 +98,9 @@ const HistoryStats: FunctionComponent = () => {
></Selector>
</SimpleGrid>
</Toolbox>
- <Box className={classes.chart} m="xs">
+ <Box className={styles.chart} m="xs">
<ResponsiveContainer>
- <BarChart className={classes.chart} data={convertedData}>
+ <BarChart className={styles.chart} data={convertedData}>
<CartesianGrid strokeDasharray="4 2"></CartesianGrid>
<XAxis dataKey="date"></XAxis>
<YAxis allowDecimals={false}></YAxis>
diff --git a/frontend/src/pages/History/history.test.tsx b/frontend/src/pages/History/history.test.tsx
index 1de1e6c5d..a348a5b91 100644
--- a/frontend/src/pages/History/history.test.tsx
+++ b/frontend/src/pages/History/history.test.tsx
@@ -1,7 +1,7 @@
import { renderTest, RenderTestCase } from "@/tests/render";
import MoviesHistoryView from "./Movies";
import SeriesHistoryView from "./Series";
-import HistoryStats from "./Statistics";
+import HistoryStats from "./Statistics/HistoryStats";
const cases: RenderTestCase[] = [
{
diff --git a/frontend/src/pages/Movies/Details/index.tsx b/frontend/src/pages/Movies/Details/index.tsx
index a6b4b0aa8..4168754f1 100644
--- a/frontend/src/pages/Movies/Details/index.tsx
+++ b/frontend/src/pages/Movies/Details/index.tsx
@@ -123,7 +123,7 @@ const MovieDetailView: FunctionComponent = () => {
<DropContent></DropContent>
</Dropzone.FullScreen>
<Toolbox>
- <Group spacing="xs">
+ <Group gap="xs">
<Toolbox.Button
icon={faSync}
disabled={hasTask}
@@ -168,7 +168,7 @@ const MovieDetailView: FunctionComponent = () => {
Manual
</Toolbox.Button>
</Group>
- <Group spacing="xs">
+ <Group gap="xs">
<Toolbox.Button
disabled={!allowEdit || movie.profileId === null || hasTask}
icon={faCloudUploadAlt}
@@ -205,7 +205,7 @@ const MovieDetailView: FunctionComponent = () => {
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
- icon={<FontAwesomeIcon icon={faToolbox} />}
+ leftSection={<FontAwesomeIcon icon={faToolbox} />}
onClick={() => {
if (movie) {
modals.openContextModal(SubtitleToolsModal, {
@@ -217,7 +217,7 @@ const MovieDetailView: FunctionComponent = () => {
Mass Edit
</Menu.Item>
<Menu.Item
- icon={<FontAwesomeIcon icon={faHistory} />}
+ leftSection={<FontAwesomeIcon icon={faHistory} />}
onClick={() => {
if (movie) {
modals.openContextModal(MovieHistoryModal, { movie });
diff --git a/frontend/src/pages/Movies/Details/table.tsx b/frontend/src/pages/Movies/Details/table.tsx
index 0a327b745..aa7e164cc 100644
--- a/frontend/src/pages/Movies/Details/table.tsx
+++ b/frontend/src/pages/Movies/Details/table.tsx
@@ -4,7 +4,6 @@ import { Action, SimpleTable } from "@/components";
import Language from "@/components/bazarr/Language";
import SubtitleToolsMenu from "@/components/SubtitleToolsMenu";
import { task, TaskGroup } from "@/modules/task";
-import { useTableStyles } from "@/styles";
import { filterSubtitleBy } from "@/utilities";
import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faEllipsis, faSearch } from "@fortawesome/free-solid-svg-icons";
@@ -40,17 +39,17 @@ const Table: FunctionComponent<Props> = ({ movie, profile, disabled }) => {
Header: "Subtitle Path",
accessor: "path",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
-
const props: TextProps = {
- className: classes.primary,
+ className: "table-primary",
};
if (isSubtitleTrack(value)) {
- return <Text {...props}>Video File Subtitle Track</Text>;
+ return (
+ <Text className="table-primary">Video File Subtitle Track</Text>
+ );
} else if (isSubtitleMissing(value)) {
return (
- <Text {...props} color="dimmed">
+ <Text {...props} c="dimmed">
{value}
</Text>
);
diff --git a/frontend/src/pages/Movies/index.tsx b/frontend/src/pages/Movies/index.tsx
index dd9f531e1..262d56195 100644
--- a/frontend/src/pages/Movies/index.tsx
+++ b/frontend/src/pages/Movies/index.tsx
@@ -6,7 +6,6 @@ import LanguageProfileName from "@/components/bazarr/LanguageProfile";
import { ItemEditModal } from "@/components/forms/ItemEditForm";
import { useModals } from "@/modules/modals";
import ItemView from "@/pages/views/ItemView";
-import { useTableStyles } from "@/styles";
import { BuildKey } from "@/utilities";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import { faBookmark, faWrench } from "@fortawesome/free-solid-svg-icons";
@@ -35,10 +34,9 @@ const MovieView: FunctionComponent = () => {
Header: "Name",
accessor: "title",
Cell: ({ row, value }) => {
- const { classes } = useTableStyles();
const target = `/movies/${row.original.radarrId}`;
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{value}
</Anchor>
);
diff --git a/frontend/src/pages/Series/index.tsx b/frontend/src/pages/Series/index.tsx
index 66921347c..60206d34a 100644
--- a/frontend/src/pages/Series/index.tsx
+++ b/frontend/src/pages/Series/index.tsx
@@ -4,7 +4,6 @@ import LanguageProfileName from "@/components/bazarr/LanguageProfile";
import { ItemEditModal } from "@/components/forms/ItemEditForm";
import { useModals } from "@/modules/modals";
import ItemView from "@/pages/views/ItemView";
-import { useTableStyles } from "@/styles";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import { faBookmark, faWrench } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -34,10 +33,9 @@ const SeriesView: FunctionComponent = () => {
Header: "Name",
accessor: "title",
Cell: ({ row, value }) => {
- const { classes } = useTableStyles();
const target = `/series/${row.original.sonarrSeriesId}`;
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{value}
</Anchor>
);
@@ -70,13 +68,14 @@ const SeriesView: FunctionComponent = () => {
}
return (
- <Progress
- key={title}
- size="xl"
- color={episodeMissingCount === 0 ? "brand" : "yellow"}
- value={progress}
- label={label}
- ></Progress>
+ <Progress.Root key={title} size="xl">
+ <Progress.Section
+ value={progress}
+ color={episodeMissingCount === 0 ? "brand" : "yellow"}
+ >
+ <Progress.Label>{label}</Progress.Label>
+ </Progress.Section>
+ </Progress.Root>
);
},
},
diff --git a/frontend/src/pages/Settings/General/index.tsx b/frontend/src/pages/Settings/General/index.tsx
index 8cc5ea8c3..bec3f946f 100644
--- a/frontend/src/pages/Settings/General/index.tsx
+++ b/frontend/src/pages/Settings/General/index.tsx
@@ -4,7 +4,7 @@ import {
faClipboard,
faSync,
} from "@fortawesome/free-solid-svg-icons";
-import { Group as MantineGroup, Text as MantineText } from "@mantine/core";
+import { Box, Group as MantineGroup, Text as MantineText } from "@mantine/core";
import { useClipboard } from "@mantine/hooks";
import { FunctionComponent, useState } from "react";
import {
@@ -54,7 +54,7 @@ const SettingsGeneralView: FunctionComponent = () => {
></Number>
<Text
label="Base URL"
- icon="/"
+ leftSection="/"
settingKey="settings-general-base_url"
settingOptions={{
onLoaded: (s) => s.general.base_url?.slice(1) ?? "",
@@ -87,7 +87,7 @@ const SettingsGeneralView: FunctionComponent = () => {
rightSectionWidth={95}
rightSectionProps={{ style: { justifyContent: "flex-end" } }}
rightSection={
- <MantineGroup spacing="xs" mx="xs" position="right">
+ <MantineGroup gap="xs" mx="xs" justify="right">
{
// Clipboard API is only available in secure contexts See: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#interfaces
window.isSecureContext && (
@@ -204,13 +204,12 @@ const SettingsGeneralView: FunctionComponent = () => {
<Number
label="Retention"
settingKey="settings-backup-retention"
- styles={{
- rightSection: { width: "4rem", justifyContent: "flex-end" },
- }}
rightSection={
- <MantineText size="xs" px="sm" color="dimmed">
- Days
- </MantineText>
+ <Box w="4rem" style={{ justifyContent: "flex-end" }}>
+ <MantineText size="xs" px="sm" c="dimmed">
+ Days
+ </MantineText>
+ </Box>
}
></Number>
</Section>
diff --git a/frontend/src/pages/Settings/Languages/equals.tsx b/frontend/src/pages/Settings/Languages/equals.tsx
index a4fe95eee..a417059ab 100644
--- a/frontend/src/pages/Settings/Languages/equals.tsx
+++ b/frontend/src/pages/Settings/Languages/equals.tsx
@@ -355,7 +355,7 @@ const EqualsTable: FunctionComponent<EqualsTableProps> = () => {
return (
<>
<SimpleTable data={equals} columns={columns}></SimpleTable>
- <Button fullWidth disabled={!canAdd} color="light" onClick={add}>
+ <Button fullWidth disabled={!canAdd} onClick={add}>
{canAdd ? "Add Equal" : "No Enabled Languages"}
</Button>
</>
diff --git a/frontend/src/pages/Settings/Languages/table.tsx b/frontend/src/pages/Settings/Languages/table.tsx
index a1ee217e8..86a227207 100644
--- a/frontend/src/pages/Settings/Languages/table.tsx
+++ b/frontend/src/pages/Settings/Languages/table.tsx
@@ -70,7 +70,7 @@ const Table: FunctionComponent = () => {
const items = row.value;
const cutoff = row.row.original.cutoff;
return (
- <Group spacing="xs" noWrap>
+ <Group gap="xs" wrap="nowrap">
{items.map((v) => {
const isCutoff = v.id === cutoff || cutoff === anyCutoff;
return (
@@ -128,7 +128,7 @@ const Table: FunctionComponent = () => {
Cell: ({ row }) => {
const profile = row.original;
return (
- <Group spacing="xs" noWrap>
+ <Group gap="xs" wrap="nowrap">
<Action
label="Edit Profile"
icon={faWrench}
@@ -163,7 +163,6 @@ const Table: FunctionComponent = () => {
<Button
fullWidth
disabled={!canAdd}
- color="light"
onClick={() => {
const profile = {
profileId: nextProfileId,
diff --git a/frontend/src/pages/Settings/Notifications/components.tsx b/frontend/src/pages/Settings/Notifications/components.tsx
index 1a2b20f65..cde3f3268 100644
--- a/frontend/src/pages/Settings/Notifications/components.tsx
+++ b/frontend/src/pages/Settings/Notifications/components.tsx
@@ -90,7 +90,7 @@ const NotificationForm: FunctionComponent<Props> = ({
></Textarea>
</div>
<Divider></Divider>
- <Group position="right">
+ <Group justify="right">
<MutateButton mutation={test} args={() => form.values.url}>
Test
</MutateButton>
diff --git a/frontend/src/pages/Settings/Providers/components.tsx b/frontend/src/pages/Settings/Providers/components.tsx
index 91076d7de..803550ce5 100644
--- a/frontend/src/pages/Settings/Providers/components.tsx
+++ b/frontend/src/pages/Settings/Providers/components.tsx
@@ -9,12 +9,12 @@ import {
Text as MantineText,
SimpleGrid,
Stack,
+ AutocompleteProps,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { capitalize } from "lodash";
import {
FunctionComponent,
- forwardRef,
useCallback,
useMemo,
useRef,
@@ -50,6 +50,11 @@ interface ProviderViewProps {
settingsKey: SettingsKey;
}
+interface ProviderSelect {
+ value: string;
+ payload: ProviderInfo;
+}
+
export const ProviderView: FunctionComponent<ProviderViewProps> = ({
availableOptions,
settingsKey,
@@ -130,17 +135,16 @@ interface ProviderToolProps {
settingsKey: Readonly<SettingsKey>;
}
-const SelectItem = forwardRef<
- HTMLDivElement,
- { payload: ProviderInfo; label: string }
->(({ payload: { description }, label, ...other }, ref) => {
+const SelectItem: AutocompleteProps["renderOption"] = ({ option }) => {
+ const provider = option as ProviderSelect;
+
return (
- <Stack spacing={1} ref={ref} {...other}>
- <MantineText size="md">{label}</MantineText>
- <MantineText size="xs">{description}</MantineText>
+ <Stack gap={1}>
+ <MantineText size="md">{provider.value}</MantineText>
+ <MantineText size="xs">{provider.payload.description}</MantineText>
</Stack>
);
-});
+};
const ProviderTool: FunctionComponent<ProviderToolProps> = ({
payload,
@@ -298,19 +302,19 @@ const ProviderTool: FunctionComponent<ProviderToolProps> = ({
}
});
- return <Stack spacing="xs">{elements}</Stack>;
+ return <Stack gap="xs">{elements}</Stack>;
}, [info]);
return (
<SettingsProvider value={settings}>
<FormContext.Provider value={form}>
<Stack>
- <Stack spacing="xs">
+ <Stack gap="xs">
<Selector
data-autofocus
searchable
placeholder="Click to Select a Provider"
- itemComponent={SelectItem}
+ renderOption={SelectItem}
disabled={payload !== null}
{...selectorOptions}
value={info}
@@ -323,7 +327,7 @@ const ProviderTool: FunctionComponent<ProviderToolProps> = ({
</div>
</Stack>
<Divider></Divider>
- <Group position="right">
+ <Group justify="right">
<Button hidden={!payload} color="red" onClick={deletePayload}>
Delete
</Button>
diff --git a/frontend/src/pages/Settings/Radarr/index.tsx b/frontend/src/pages/Settings/Radarr/index.tsx
index 8cd038ab8..8805aeed8 100644
--- a/frontend/src/pages/Settings/Radarr/index.tsx
+++ b/frontend/src/pages/Settings/Radarr/index.tsx
@@ -30,7 +30,7 @@ const SettingsRadarrView: FunctionComponent = () => {
<Number label="Port" settingKey="settings-radarr-port"></Number>
<Text
label="Base URL"
- icon="/"
+ leftSection="/"
settingKey="settings-radarr-base_url"
settingOptions={{
onLoaded: (s) => s.radarr.base_url?.slice(1) ?? "",
diff --git a/frontend/src/pages/Settings/Sonarr/index.tsx b/frontend/src/pages/Settings/Sonarr/index.tsx
index 1d2125568..0f60d70a8 100644
--- a/frontend/src/pages/Settings/Sonarr/index.tsx
+++ b/frontend/src/pages/Settings/Sonarr/index.tsx
@@ -32,7 +32,7 @@ const SettingsSonarrView: FunctionComponent = () => {
<Number label="Port" settingKey="settings-sonarr-port"></Number>
<Text
label="Base URL"
- icon="/"
+ leftSection="/"
settingKey="settings-sonarr-base_url"
settingOptions={{
onLoaded: (s) => s.sonarr.base_url?.slice(1) ?? "",
diff --git a/frontend/src/pages/Settings/Subtitles/index.tsx b/frontend/src/pages/Settings/Subtitles/index.tsx
index f2dec69ac..c4dffd227 100644
--- a/frontend/src/pages/Settings/Subtitles/index.tsx
+++ b/frontend/src/pages/Settings/Subtitles/index.tsx
@@ -501,7 +501,7 @@ const SettingsSubtitlesView: FunctionComponent = () => {
label="Command"
settingKey="settings-general-postprocessing_cmd"
></Text>
- <Table highlightOnHover fontSize="sm">
+ <Table highlightOnHover fs="sm">
<tbody>{commandOptionElements}</tbody>
</Table>
</CollapseBox>
diff --git a/frontend/src/pages/Settings/components/Card.module.scss b/frontend/src/pages/Settings/components/Card.module.scss
new file mode 100644
index 000000000..746e55e65
--- /dev/null
+++ b/frontend/src/pages/Settings/components/Card.module.scss
@@ -0,0 +1,9 @@
+.card {
+ border-radius: var(--mantine-radius-sm);
+ border: 1px solid var(--mantine-color-gray-7);
+
+ &:hover {
+ box-shadow: var(--mantine-shadow-md);
+ border: 1px solid $color-brand-5;
+ }
+}
diff --git a/frontend/src/pages/Settings/components/Card.tsx b/frontend/src/pages/Settings/components/Card.tsx
index 4f3bd4fbf..6e4b58f47 100644
--- a/frontend/src/pages/Settings/components/Card.tsx
+++ b/frontend/src/pages/Settings/components/Card.tsx
@@ -1,30 +1,8 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import {
- Center,
- createStyles,
- Stack,
- Text,
- UnstyledButton,
-} from "@mantine/core";
+import { Center, Stack, Text, UnstyledButton } from "@mantine/core";
import { FunctionComponent } from "react";
-
-const useCardStyles = createStyles((theme) => {
- return {
- card: {
- borderRadius: theme.radius.sm,
- border: `1px solid ${theme.colors.gray[7]}`,
-
- "&:hover": {
- boxShadow: theme.shadows.md,
- border: `1px solid ${theme.colors.brand[5]}`,
- },
- },
- stack: {
- height: "100%",
- },
- };
-});
+import styles from "./Card.module.scss";
interface CardProps {
header?: string;
@@ -39,16 +17,15 @@ export const Card: FunctionComponent<CardProps> = ({
plus,
onClick,
}) => {
- const { classes } = useCardStyles();
return (
- <UnstyledButton p="lg" onClick={onClick} className={classes.card}>
+ <UnstyledButton p="lg" onClick={onClick} className={styles.card}>
{plus ? (
<Center>
<FontAwesomeIcon size="2x" icon={faPlus}></FontAwesomeIcon>
</Center>
) : (
- <Stack className={classes.stack} spacing={0} align="flex-start">
- <Text weight="bold">{header}</Text>
+ <Stack h="100%" gap={0} align="flex-start">
+ <Text fw="bold">{header}</Text>
<Text hidden={description === undefined}>{description}</Text>
</Stack>
)}
diff --git a/frontend/src/pages/Settings/components/Layout.tsx b/frontend/src/pages/Settings/components/Layout.tsx
index b20c8092b..5fb6dbbed 100644
--- a/frontend/src/pages/Settings/components/Layout.tsx
+++ b/frontend/src/pages/Settings/components/Layout.tsx
@@ -73,7 +73,7 @@ const Layout: FunctionComponent<Props> = (props) => {
icon={faSave}
loading={isMutating}
disabled={totalStagedCount === 0}
- rightIcon={
+ rightSection={
<Badge size="xs" radius="sm" hidden={totalStagedCount === 0}>
{totalStagedCount}
</Badge>
diff --git a/frontend/src/pages/Settings/components/LayoutModal.tsx b/frontend/src/pages/Settings/components/LayoutModal.tsx
index cb4d5a1b5..6150295e4 100644
--- a/frontend/src/pages/Settings/components/LayoutModal.tsx
+++ b/frontend/src/pages/Settings/components/LayoutModal.tsx
@@ -74,7 +74,7 @@ const LayoutModal: FunctionComponent<Props> = (props) => {
<Space h="md" />
<Divider></Divider>
<Space h="md" />
- <Group position="right">
+ <Group justify="right">
<Button
type="submit"
disabled={totalStagedCount === 0}
diff --git a/frontend/src/pages/Settings/components/Message.tsx b/frontend/src/pages/Settings/components/Message.tsx
index 301df7bab..eac84131b 100644
--- a/frontend/src/pages/Settings/components/Message.tsx
+++ b/frontend/src/pages/Settings/components/Message.tsx
@@ -12,7 +12,7 @@ export const Message: FunctionComponent<Props> = ({
children,
}) => {
return (
- <Text size="sm" color={type === "info" ? "dimmed" : "yellow"} my={0}>
+ <Text size="sm" c={type === "info" ? "dimmed" : "yellow"} my={0}>
{children}
</Text>
);
diff --git a/frontend/src/pages/Settings/components/Section.test.tsx b/frontend/src/pages/Settings/components/Section.test.tsx
index e7f270e0d..850a47671 100644
--- a/frontend/src/pages/Settings/components/Section.test.tsx
+++ b/frontend/src/pages/Settings/components/Section.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, screen } from "@/tests";
+import { render, screen } from "@/tests";
import { Text } from "@mantine/core";
import { describe, it } from "vitest";
import { Section } from "./Section";
@@ -6,7 +6,7 @@ import { Section } from "./Section";
describe("Settings section", () => {
const header = "Section Header";
it("should show header", () => {
- rawRender(<Section header="Section Header"></Section>);
+ render(<Section header="Section Header"></Section>);
expect(screen.getByText(header)).toBeDefined();
expect(screen.getByRole("separator")).toBeDefined();
@@ -14,7 +14,7 @@ describe("Settings section", () => {
it("should show children", () => {
const text = "Section Child";
- rawRender(
+ render(
<Section header="Section Header">
<Text>{text}</Text>
</Section>,
@@ -26,7 +26,7 @@ describe("Settings section", () => {
it("should work with hidden", () => {
const text = "Section Child";
- rawRender(
+ render(
<Section header="Section Header" hidden>
<Text>{text}</Text>
</Section>,
diff --git a/frontend/src/pages/Settings/components/Section.tsx b/frontend/src/pages/Settings/components/Section.tsx
index 36f56ff8d..7c5727bf0 100644
--- a/frontend/src/pages/Settings/components/Section.tsx
+++ b/frontend/src/pages/Settings/components/Section.tsx
@@ -14,7 +14,7 @@ export const Section: FunctionComponent<Props> = ({
children,
}) => {
return (
- <Stack hidden={hidden} spacing="xs" my="lg">
+ <Stack hidden={hidden} gap="xs" my="lg">
<Title order={4}>{header}</Title>
<Divider></Divider>
{children}
diff --git a/frontend/src/pages/Settings/components/collapse.tsx b/frontend/src/pages/Settings/components/collapse.tsx
index 1dcffbd97..c598105bd 100644
--- a/frontend/src/pages/Settings/components/collapse.tsx
+++ b/frontend/src/pages/Settings/components/collapse.tsx
@@ -31,7 +31,7 @@ const CollapseBox: FunctionComponent<Props> = ({
return (
<Collapse in={open} pl={indent ? "md" : undefined}>
- <Stack spacing="xs">{children}</Stack>
+ <Stack gap="xs">{children}</Stack>
</Collapse>
);
};
diff --git a/frontend/src/pages/Settings/components/forms.test.tsx b/frontend/src/pages/Settings/components/forms.test.tsx
index 19c66ade0..26974bad9 100644
--- a/frontend/src/pages/Settings/components/forms.test.tsx
+++ b/frontend/src/pages/Settings/components/forms.test.tsx
@@ -1,4 +1,4 @@
-import { rawRender, RenderOptions, screen } from "@/tests";
+import { render, RenderOptions, screen } from "@/tests";
import { useForm } from "@mantine/form";
import { FunctionComponent, PropsWithChildren, ReactElement } from "react";
import { describe, it } from "vitest";
@@ -18,7 +18,7 @@ const FormSupport: FunctionComponent<PropsWithChildren> = ({ children }) => {
const formRender = (
ui: ReactElement,
options?: Omit<RenderOptions, "wrapper">,
-) => rawRender(ui, { wrapper: FormSupport, ...options });
+) => render(<FormSupport>{ui}</FormSupport>);
describe("Settings form", () => {
describe("number component", () => {
diff --git a/frontend/src/pages/Settings/components/forms.tsx b/frontend/src/pages/Settings/components/forms.tsx
index 3e1d3f12f..1d65b0375 100644
--- a/frontend/src/pages/Settings/components/forms.tsx
+++ b/frontend/src/pages/Settings/components/forms.tsx
@@ -38,6 +38,11 @@ export const Number: FunctionComponent<NumberProps> = (props) => {
if (val === "") {
val = 0;
}
+
+ if (typeof val === "string") {
+ return update(+val);
+ }
+
update(val);
}}
></NumberInput>
diff --git a/frontend/src/pages/Settings/components/index.tsx b/frontend/src/pages/Settings/components/index.tsx
index 99d1658bc..6ac9cb86c 100644
--- a/frontend/src/pages/Settings/components/index.tsx
+++ b/frontend/src/pages/Settings/components/index.tsx
@@ -56,7 +56,7 @@ export const URLTestButton: FunctionComponent<{
}, [address, port, url, apikey, ssl]);
return (
- <Button onClick={click} color={color} title={title}>
+ <Button autoContrast onClick={click} variant={color} title={title}>
{title}
</Button>
);
@@ -107,7 +107,7 @@ export const ProviderTestButton: FunctionComponent<{
}, [testUrl]);
return (
- <Button onClick={click} color={color} title={title}>
+ <Button onClick={click} variant={color} title={title}>
{title}
</Button>
);
diff --git a/frontend/src/pages/Settings/components/pathMapper.tsx b/frontend/src/pages/Settings/components/pathMapper.tsx
index 8bb3514b7..38fbb27e9 100644
--- a/frontend/src/pages/Settings/components/pathMapper.tsx
+++ b/frontend/src/pages/Settings/components/pathMapper.tsx
@@ -141,7 +141,7 @@ export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
columns={columns}
data={data}
></SimpleTable>
- <Button fullWidth color="light" onClick={addRow}>
+ <Button fullWidth onClick={addRow}>
Add
</Button>
</>
diff --git a/frontend/src/pages/System/Announcements/table.tsx b/frontend/src/pages/System/Announcements/table.tsx
index 74a160190..eb9b6e43c 100644
--- a/frontend/src/pages/System/Announcements/table.tsx
+++ b/frontend/src/pages/System/Announcements/table.tsx
@@ -1,7 +1,6 @@
import { useSystemAnnouncementsAddDismiss } from "@/apis/hooks";
import { SimpleTable } from "@/components";
import { MutateAction } from "@/components/async";
-import { useTableStyles } from "@/styles";
import { faWindowClose } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
@@ -20,16 +19,14 @@ const Table: FunctionComponent<Props> = ({ announcements }) => {
Header: "Since",
accessor: "timestamp",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.primary}>{value}</Text>;
+ return <Text className="table-primary">{value}</Text>;
},
},
{
Header: "Announcement",
accessor: "text",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.primary}>{value}</Text>;
+ return <Text className="table-primary">{value}</Text>;
},
},
{
diff --git a/frontend/src/pages/System/Backups/table.tsx b/frontend/src/pages/System/Backups/table.tsx
index 4f9eeae44..42236cee5 100644
--- a/frontend/src/pages/System/Backups/table.tsx
+++ b/frontend/src/pages/System/Backups/table.tsx
@@ -1,7 +1,6 @@
import { useDeleteBackups, useRestoreBackups } from "@/apis/hooks";
import { Action, PageTable } from "@/components";
import { useModals } from "@/modules/modals";
-import { useTableStyles } from "@/styles";
import { Environment } from "@/utilities";
import { faHistory, faTrash } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
@@ -32,16 +31,14 @@ const Table: FunctionComponent<Props> = ({ backups }) => {
Header: "Size",
accessor: "size",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}</Text>;
+ return <Text className="table-no-wrap">{value}</Text>;
},
},
{
Header: "Time",
accessor: "date",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}</Text>;
+ return <Text className="table-no-wrap">{value}</Text>;
},
},
{
diff --git a/frontend/src/pages/System/Logs/index.tsx b/frontend/src/pages/System/Logs/index.tsx
index d77e102d8..a232b8069 100644
--- a/frontend/src/pages/System/Logs/index.tsx
+++ b/frontend/src/pages/System/Logs/index.tsx
@@ -86,7 +86,7 @@ const SystemLogsView: FunctionComponent = () => {
<Container fluid px={0}>
<QueryOverlay result={logs}>
<Toolbox>
- <Group spacing="xs">
+ <Group gap="xs">
<Toolbox.Button
loading={isFetching}
icon={faSync}
@@ -108,7 +108,7 @@ const SystemLogsView: FunctionComponent = () => {
loading={isLoading}
icon={faFilter}
onClick={openFilterModal}
- rightIcon={
+ rightSection={
suffix() !== "" ? (
<Badge size="xs" radius="sm">
{suffix()}
diff --git a/frontend/src/pages/System/Releases/index.tsx b/frontend/src/pages/System/Releases/index.tsx
index f205da086..66b5a1c8a 100644
--- a/frontend/src/pages/System/Releases/index.tsx
+++ b/frontend/src/pages/System/Releases/index.tsx
@@ -23,7 +23,7 @@ const SystemReleasesView: FunctionComponent = () => {
return (
<Container size={600} py={12}>
<QueryOverlay result={releases}>
- <Stack spacing="lg">
+ <Stack gap="lg">
{data?.map((v, idx) => (
<ReleaseCard key={BuildKey(idx, v.date)} {...v}></ReleaseCard>
))}
@@ -47,7 +47,7 @@ const ReleaseCard: FunctionComponent<ReleaseInfo> = ({
return (
<Card shadow="md" p="lg">
<Group>
- <Text weight="bold">{name}</Text>
+ <Text fw="bold">{name}</Text>
<Badge color="blue">{date}</Badge>
<Badge color={prerelease ? "yellow" : "green"}>
{prerelease ? "Development" : "Master"}
diff --git a/frontend/src/pages/System/Status/index.tsx b/frontend/src/pages/System/Status/index.tsx
index 49c88ccd4..05996f7ad 100644
--- a/frontend/src/pages/System/Status/index.tsx
+++ b/frontend/src/pages/System/Status/index.tsx
@@ -46,7 +46,7 @@ function Row(props: InfoProps): JSX.Element {
return (
<Grid columns={10}>
<Grid.Col span={2}>
- <Text size="sm" align="right" weight="bold">
+ <Text size="sm" ta="right" fw="bold">
{title}
</Text>
</Grid.Col>
@@ -85,9 +85,12 @@ const InfoContainer: FunctionComponent<
return (
<Stack>
<Divider
- labelProps={{ size: "medium", weight: "bold" }}
labelPosition="left"
- label={title}
+ label={
+ <Text size="md" fw="bold">
+ {title}
+ </Text>
+ }
></Divider>
{children}
<Space />
diff --git a/frontend/src/pages/System/Status/table.tsx b/frontend/src/pages/System/Status/table.tsx
index 3b8a87e8a..c139f9d93 100644
--- a/frontend/src/pages/System/Status/table.tsx
+++ b/frontend/src/pages/System/Status/table.tsx
@@ -1,5 +1,4 @@
import { SimpleTable } from "@/components";
-import { useTableStyles } from "@/styles";
import { Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
@@ -15,16 +14,14 @@ const Table: FunctionComponent<Props> = ({ health }) => {
Header: "Object",
accessor: "object",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}</Text>;
+ return <Text className="table-no-wrap">{value}</Text>;
},
},
{
Header: "Issue",
accessor: "issue",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.primary}>{value}</Text>;
+ return <Text className="table-primary">{value}</Text>;
},
},
],
diff --git a/frontend/src/pages/System/Tasks/table.tsx b/frontend/src/pages/System/Tasks/table.tsx
index ea45af49d..064438668 100644
--- a/frontend/src/pages/System/Tasks/table.tsx
+++ b/frontend/src/pages/System/Tasks/table.tsx
@@ -1,7 +1,6 @@
import { useRunTask } from "@/apis/hooks";
import { SimpleTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
-import { useTableStyles } from "@/styles";
import { faPlay } from "@fortawesome/free-solid-svg-icons";
import { Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
@@ -18,16 +17,14 @@ const Table: FunctionComponent<Props> = ({ tasks }) => {
Header: "Name",
accessor: "name",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.primary}>{value}</Text>;
+ return <Text className="table-primary">{value}</Text>;
},
},
{
Header: "Interval",
accessor: "interval",
Cell: ({ value }) => {
- const { classes } = useTableStyles();
- return <Text className={classes.noWrap}>{value}</Text>;
+ return <Text className="table-no-wrap">{value}</Text>;
},
},
{
diff --git a/frontend/src/pages/Wanted/Movies/index.tsx b/frontend/src/pages/Wanted/Movies/index.tsx
index 57fb6d6ed..d62871416 100644
--- a/frontend/src/pages/Wanted/Movies/index.tsx
+++ b/frontend/src/pages/Wanted/Movies/index.tsx
@@ -39,7 +39,7 @@ const WantedMoviesView: FunctionComponent = () => {
const { download } = useMovieSubtitleModification();
return (
- <Group spacing="sm">
+ <Group gap="sm">
{value.map((item, idx) => (
<Badge
color={download.isLoading ? "gray" : undefined}
diff --git a/frontend/src/pages/Wanted/Series/index.tsx b/frontend/src/pages/Wanted/Series/index.tsx
index 96507bccd..d5f4d8993 100644
--- a/frontend/src/pages/Wanted/Series/index.tsx
+++ b/frontend/src/pages/Wanted/Series/index.tsx
@@ -6,7 +6,6 @@ import {
import Language from "@/components/bazarr/Language";
import { TaskGroup, task } from "@/modules/task";
import WantedView from "@/pages/views/WantedView";
-import { useTableStyles } from "@/styles";
import { BuildKey } from "@/utilities";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -23,9 +22,8 @@ const WantedSeriesView: FunctionComponent = () => {
accessor: "seriesTitle",
Cell: (row) => {
const target = `/series/${row.row.original.sonarrSeriesId}`;
- const { classes } = useTableStyles();
return (
- <Anchor className={classes.primary} component={Link} to={target}>
+ <Anchor className="table-primary" component={Link} to={target}>
{row.value}
</Anchor>
);
@@ -49,7 +47,7 @@ const WantedSeriesView: FunctionComponent = () => {
const { download } = useEpisodeSubtitleModification();
return (
- <Group spacing="sm">
+ <Group gap="sm">
{value.map((item, idx) => (
<Badge
color={download.isLoading ? "gray" : undefined}
diff --git a/frontend/src/pages/errors/UIError.tsx b/frontend/src/pages/errors/UIError.tsx
index 4f26d0d0c..55e0d5414 100644
--- a/frontend/src/pages/errors/UIError.tsx
+++ b/frontend/src/pages/errors/UIError.tsx
@@ -45,13 +45,11 @@ const UIError: FunctionComponent<Props> = ({ error }) => {
<Center my="xl">
<Code>{stack}</Code>
</Center>
- <Group position="center">
+ <Group justify="center">
<Anchor href={`${GithubRepoRoot}/issues/new/choose`} target="_blank">
<Button color="yellow">Report Issue</Button>
</Anchor>
- <Button onClick={Reload} color="light">
- Reload Page
- </Button>
+ <Button onClick={Reload}>Reload Page</Button>
</Group>
</Container>
);
diff --git a/frontend/src/pages/views/ItemOverview.tsx b/frontend/src/pages/views/ItemOverview.tsx
index d95944db3..a5224b586 100644
--- a/frontend/src/pages/views/ItemOverview.tsx
+++ b/frontend/src/pages/views/ItemOverview.tsx
@@ -28,11 +28,9 @@ import {
HoverCard,
Image,
List,
- MediaQuery,
Stack,
Text,
Title,
- createStyles,
} from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
@@ -41,25 +39,9 @@ interface Props {
details?: { icon: IconDefinition; text: string }[];
}
-const useStyles = createStyles((theme) => {
- return {
- poster: {
- maxWidth: "250px",
- },
- col: {
- maxWidth: "100%",
- },
- group: {
- maxWidth: "100%",
- },
- };
-});
-
const ItemOverview: FunctionComponent<Props> = (props) => {
const { item, details } = props;
- const { classes } = useStyles();
-
const detailBadges = useMemo(() => {
const badges: (JSX.Element | null)[] = [];
@@ -150,21 +132,19 @@ const ItemOverview: FunctionComponent<Props> = (props) => {
flexWrap: "nowrap",
}}
>
- <MediaQuery smallerThan="sm" styles={{ display: "none" }}>
- <Grid.Col span={3}>
- <Image
- src={item?.poster}
- mx="auto"
- className={classes.poster}
- withPlaceholder
- ></Image>
- </Grid.Col>
- </MediaQuery>
- <Grid.Col span={8} className={classes.col}>
- <Stack align="flex-start" spacing="xs" mx={6}>
- <Group align="flex-start" noWrap className={classes.group}>
+ <Grid.Col span={3} hiddenFrom="sm">
+ <Image
+ src={item?.poster}
+ mx="auto"
+ maw="250px"
+ fallbackSrc="https://placehold.co/250x250?text=Placeholder"
+ ></Image>
+ </Grid.Col>
+ <Grid.Col span={8} maw="100%">
+ <Stack align="flex-start" gap="xs" mx={6}>
+ <Group align="flex-start" wrap="nowrap" maw="100%">
<Title my={0}>
- <Text inherit color="white">
+ <Text inherit c="white">
<Box component="span" mr={12}>
<FontAwesomeIcon
title={item?.monitored ? "monitored" : "unmonitored"}
@@ -176,10 +156,7 @@ const ItemOverview: FunctionComponent<Props> = (props) => {
</Title>
<HoverCard position="bottom" withArrow>
<HoverCard.Target>
- <Text
- hidden={item?.alternativeTitles.length === 0}
- color="white"
- >
+ <Text hidden={item?.alternativeTitles.length === 0} c="white">
<FontAwesomeIcon icon={faClone} />
</Text>
</HoverCard.Target>
@@ -192,16 +169,16 @@ const ItemOverview: FunctionComponent<Props> = (props) => {
</HoverCard.Dropdown>
</HoverCard>
</Group>
- <Group spacing="xs" className={classes.group}>
+ <Group gap="xs" maw="100%">
{detailBadges}
</Group>
- <Group spacing="xs" className={classes.group}>
+ <Group gap="xs" maw="100%">
{audioBadges}
</Group>
- <Group spacing="xs" className={classes.group}>
+ <Group gap="xs" maw="100%">
{languageBadges}
</Group>
- <Text size="sm" color="white">
+ <Text size="sm" c="white">
{item?.overview}
</Text>
</Stack>
diff --git a/frontend/src/pages/views/MassEditor.tsx b/frontend/src/pages/views/MassEditor.tsx
index b15a55e83..ddab269bd 100644
--- a/frontend/src/pages/views/MassEditor.tsx
+++ b/frontend/src/pages/views/MassEditor.tsx
@@ -36,13 +36,8 @@ function MassEditor<T extends Item.Base>(props: MassEditorProps<T>) {
const profileOptions = useSelectorOptions(profiles ?? [], (v) => v.name);
- const profileOptionsWithAction = useMemo<
- SelectorOption<Language.Profile | null>[]
- >(
- () => [
- { label: "Clear", value: null, group: "Action" },
- ...profileOptions.options,
- ],
+ const profileOptionsWithAction = useMemo<SelectorOption<Language.Profile>[]>(
+ () => [...profileOptions.options],
[profileOptions.options],
);
@@ -82,6 +77,7 @@ function MassEditor<T extends Item.Base>(props: MassEditorProps<T>) {
},
[selections],
);
+
return (
<Container fluid px={0}>
<Toolbox>
diff --git a/frontend/src/providers.tsx b/frontend/src/providers.tsx
index 97189a23e..aa526c25b 100644
--- a/frontend/src/providers.tsx
+++ b/frontend/src/providers.tsx
@@ -1,4 +1,3 @@
-import ThemeProvider from "@/App/theme";
import queryClient from "@/apis/queries";
import { ModalsProvider } from "@/modules/modals";
import "@fontsource/roboto/300.css";
@@ -7,6 +6,7 @@ import { FunctionComponent, PropsWithChildren } from "react";
import { QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import { Environment } from "./utilities";
+import ThemeProvider from "@/App/ThemeProvider";
export const AllProviders: FunctionComponent<PropsWithChildren> = ({
children,
diff --git a/frontend/src/styles/index.ts b/frontend/src/styles/index.ts
deleted file mode 100644
index 0e948df9e..000000000
--- a/frontend/src/styles/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./table";
diff --git a/frontend/src/styles/table.ts b/frontend/src/styles/table.ts
deleted file mode 100644
index 40d533401..000000000
--- a/frontend/src/styles/table.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { createStyles } from "@mantine/core";
-
-export const useTableStyles = createStyles((theme) => ({
- primary: {
- display: "inline-block",
- [theme.fn.smallerThan("sm")]: {
- minWidth: "12rem",
- },
- },
- noWrap: {
- whiteSpace: "nowrap",
- },
- select: {
- display: "inline-block",
- [theme.fn.smallerThan("sm")]: {
- minWidth: "10rem",
- },
- },
-}));
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 41fbaa7a4..e11fa90b3 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -31,6 +31,16 @@ export default defineConfig(async ({ mode, command }) => {
enableBuild: false,
}),
],
+ css: {
+ preprocessorOptions: {
+ scss: {
+ additionalData: `
+ @import "./src/assets/_mantine";
+ @import "./src/assets/_bazarr";
+ `,
+ },
+ },
+ },
base: "./",
resolve: {
alias: {