diff --git a/hom_button_2.png b/hom_button_2.png new file mode 100644 index 0000000..8c93774 Binary files /dev/null and b/hom_button_2.png differ diff --git a/home_button.jpg b/home_button.jpg new file mode 100644 index 0000000..2f85444 Binary files /dev/null and b/home_button.jpg differ diff --git a/package-lock.json b/package-lock.json index 7314fd9..3f50850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,11 @@ "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@fontsource/roboto": "^4.5.8", + "@material-ui/core": "^4.12.4", "@mui/icons-material": "^5.11.11", "@mui/material": "^5.11.11", "@mui/styled-engine-sc": "^5.11.11", + "@mui/x-data-grid": "^6.0.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -3113,6 +3115,157 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@material-ui/core": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", + "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.5", + "@material-ui/system": "^4.12.2", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/styles/node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@material-ui/styles/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/system/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "dependencies": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, "node_modules/@mui/base": { "version": "5.0.0-alpha.119", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz", @@ -3368,9 +3521,9 @@ } }, "node_modules/@mui/utils": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.11.tgz", - "integrity": "sha512-neMM5rrEXYQrOrlxUfns/TGgX4viS8K2zb9pbQh11/oUUYFlGI32Tn+PHePQx7n6Fy/0zq6WxdBFC9VpnJ5JrQ==", + "version": "5.11.13", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz", + "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==", "dependencies": { "@babel/runtime": "^7.21.0", "@types/prop-types": "^15.7.5", @@ -3394,6 +3547,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/@mui/x-data-grid": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.0.4.tgz", + "integrity": "sha512-SPkxmPhVU0hJEun/Ju5do5CPVzJj4NHT5XEPpfvDyVhHR4p4ax/EiXbtmLBl5d+D/tA8uaLEK0PUQWBLC8JNQA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.11.13", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "reselect": "^4.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.4.1", + "@mui/system": "^5.4.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -6792,6 +6970,15 @@ "node": ">=0.10.0" } }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -9539,6 +9726,11 @@ "node": ">=10.17.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -9854,6 +10046,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, "node_modules/is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -12317,6 +12514,88 @@ "node": ">=0.10.0" } }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -13399,6 +13678,11 @@ "node": ">=4" } }, + "node_modules/popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, "node_modules/postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -15406,6 +15690,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", + "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -16876,19 +17165,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -19379,14 +19655,12 @@ "@csstools/postcss-unset-value": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "requires": {} + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" }, "@csstools/selector-specificity": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", - "requires": {} + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==" }, "@emotion/babel-plugin": { "version": "11.10.6", @@ -19506,8 +19780,7 @@ "@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", - "requires": {} + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==" }, "@emotion/utils": { "version": "1.2.0", @@ -20159,6 +20432,93 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@material-ui/core": { + "version": "4.12.4", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", + "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.5", + "@material-ui/system": "^4.12.2", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styles": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", + "prop-types": "^15.7.2" + }, + "dependencies": { + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + } + } + }, + "@material-ui/system": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "dependencies": { + "csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + } + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" + }, + "@material-ui/utils": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@mui/base": { "version": "5.0.0-alpha.119", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz", @@ -20268,13 +20628,12 @@ "@mui/types": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.3.tgz", - "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==", - "requires": {} + "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==" }, "@mui/utils": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.11.tgz", - "integrity": "sha512-neMM5rrEXYQrOrlxUfns/TGgX4viS8K2zb9pbQh11/oUUYFlGI32Tn+PHePQx7n6Fy/0zq6WxdBFC9VpnJ5JrQ==", + "version": "5.11.13", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.13.tgz", + "integrity": "sha512-5ltA58MM9euOuUcnvwFJqpLdEugc9XFsRR8Gt4zZNb31XzMfSKJPR4eumulyhsOTK1rWf7K4D63NKFPfX0AxqA==", "requires": { "@babel/runtime": "^7.21.0", "@types/prop-types": "^15.7.5", @@ -20290,6 +20649,18 @@ } } }, + "@mui/x-data-grid": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.0.4.tgz", + "integrity": "sha512-SPkxmPhVU0hJEun/Ju5do5CPVzJj4NHT5XEPpfvDyVhHR4p4ax/EiXbtmLBl5d+D/tA8uaLEK0PUQWBLC8JNQA==", + "requires": { + "@babel/runtime": "^7.21.0", + "@mui/utils": "^5.11.13", + "clsx": "^1.2.1", + "prop-types": "^15.8.1", + "reselect": "^4.1.7" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -21599,14 +21970,12 @@ "acorn-import-assertions": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "requires": {} + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "acorn-node": { "version": "1.8.2", @@ -21692,8 +22061,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "ansi-escapes": { "version": "4.3.2", @@ -22004,8 +22372,7 @@ "babel-plugin-named-asset-import": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "requires": {} + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" }, "babel-plugin-polyfill-corejs2": { "version": "0.3.3", @@ -22646,8 +23013,7 @@ "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", - "requires": {} + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" }, "css-has-pseudo": { "version": "3.0.4", @@ -22753,8 +23119,7 @@ "css-prefers-color-scheme": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "requires": {} + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" }, "css-select": { "version": "4.3.0", @@ -22799,6 +23164,15 @@ } } }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -22868,8 +23242,7 @@ "cssnano-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "requires": {} + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" }, "csso": { "version": "4.2.0", @@ -23765,8 +24138,7 @@ "eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" }, "eslint-plugin-testing-library": { "version": "5.10.2", @@ -24818,6 +25190,11 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -24829,8 +25206,7 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "idb": { "version": "7.1.1", @@ -25030,6 +25406,11 @@ "is-extglob": "^2.1.1" } }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + }, "is-map": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", @@ -25923,8 +26304,7 @@ "jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "requires": {} + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==" }, "jest-regex-util": { "version": "27.5.1", @@ -26813,6 +27193,84 @@ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" }, + "jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, "jsx-ast-utils": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", @@ -27595,6 +28053,11 @@ } } }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, "postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -27616,8 +28079,7 @@ "postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "requires": {} + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" }, "postcss-calc": { "version": "8.2.4", @@ -27715,26 +28177,22 @@ "postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "requires": {} + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "requires": {} + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "requires": {} + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "requires": {} + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" }, "postcss-double-position-gradients": { "version": "3.1.2", @@ -27756,8 +28214,7 @@ "postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "requires": {} + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" }, "postcss-focus-visible": { "version": "6.0.4", @@ -27778,14 +28235,12 @@ "postcss-font-variant": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "requires": {} + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "requires": {} + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" }, "postcss-image-set-function": { "version": "4.0.7", @@ -27808,8 +28263,7 @@ "postcss-initial": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "requires": {} + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" }, "postcss-js": { "version": "4.0.1", @@ -27873,14 +28327,12 @@ "postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "requires": {} + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "requires": {} + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" }, "postcss-merge-longhand": { "version": "5.1.7", @@ -27941,8 +28393,7 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "requires": {} + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -28000,8 +28451,7 @@ "postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "requires": {} + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -28072,8 +28522,7 @@ "postcss-opacity-percentage": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "requires": {} + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==" }, "postcss-ordered-values": { "version": "5.1.3", @@ -28095,8 +28544,7 @@ "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "requires": {} + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" }, "postcss-place": { "version": "7.0.5", @@ -28190,8 +28638,7 @@ "postcss-replace-overflow-wrap": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "requires": {} + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" }, "postcss-selector-not": { "version": "6.0.1", @@ -28493,8 +28940,7 @@ "react-chartjs-2": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", - "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", - "requires": {} + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==" }, "react-dev-utils": { "version": "12.0.1", @@ -28883,6 +29329,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "reselect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", + "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -29526,8 +29977,7 @@ "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "requires": {} + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" }, "styled-components": { "version": "5.3.8", @@ -29964,12 +30414,6 @@ "is-typedarray": "^1.0.0" } }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -30350,8 +30794,7 @@ "ws": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", - "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", - "requires": {} + "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==" } } }, @@ -30809,8 +31252,7 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" }, "xml-name-validator": { "version": "3.0.0", diff --git a/package.json b/package.json index a82d0c9..c76c035 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,11 @@ "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@fontsource/roboto": "^4.5.8", + "@material-ui/core": "^4.12.4", "@mui/icons-material": "^5.11.11", "@mui/material": "^5.11.11", "@mui/styled-engine-sc": "^5.11.11", + "@mui/x-data-grid": "^6.0.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/src/App.css b/src/App.css index 4e7bc89..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -1,159 +0,0 @@ -.app-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - -} - -.nav-bar { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - background-color: #e3ebda; - padding: 10px; - height: 5%; - -} - -/* .search-container { - background-color: #E0E0E0; - display: flex; - align-items: center; - width: 50%; - height: 30px; - margin-right: auto; - margin-left: auto; - border: 1px solid gray; - justify-content: center; -} */ - - -/* .search-container input { - width: 100%; -} */ - -.search-container { - display: content; - flex-direction: row; - align-items: center; - justify-content: center; - margin-top: 5px; - width: 70%; - margin-right: auto; - margin-left: auto; -} - -.search-container input { - width: 90%; -} - -.book-container { - display: flex; - flex-direction: column; - align-items: end; - justify-content: end; - width: 500px; - height: 400px; - margin: 10px; - border: 10px solid black; - border-radius: 20px; -} - -.book-name { - font-size: 20px; - font-weight: bold; - margin-bottom: 10px; -} - -.book-genre { - font-size: 16px; - margin-bottom: 10px; -} - - -.query-seed-button { - width: 90%; - height: 20px; - background-color: lightblue; - border: 1px solid black; - border-radius: 10px; - font-size: 12px; - font-weight: bold; - cursor: pointer; -} - -.grid-container { - display: grid; - grid-template-columns: repeat(6, 1fr); - grid-template-rows: repeat(3, 1fr); - grid-gap: 2%; - width: 77%; - -} - -.grid-item { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 2px; - background-color: #fff; - border-radius: 10px; - cursor: pointer; -} - -.global-explanation-container { - padding: 20px; - text-align: center; - height: 200px; - display: flex; -} - -.local-explanation-container { - padding: 20px; - text-align: center; - height: 200px; - display: flex; -} - -.side-bar-container { - background-color: #e3ebda; - padding: 5px; - width: 23%; - position: absolute; - right: 0; - top: 7%; - bottom: 0; -} - -.main-screen{ - padding-left: 2%; - padding-top: 1%; - width: 100% -} - -.book-object { - width: 73%; - height: 76%; - margin: 10px; - border: 1px #ccc; - border-radius: 5px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.book-object img { - width: 100%; - height: 100%; - align-items: flex-start; - object-fit: cover; - border-radius: 1px 1px 0 0; -} - -.book-object p { - margin: 1px 0; -} \ No newline at end of file diff --git a/src/App.js b/src/App.js index cf4c8c4..17ea138 100644 --- a/src/App.js +++ b/src/App.js @@ -1,430 +1,11 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate } from "react-router-dom"; -import axios from "axios"; +import AppRoutes from "./routes/AppRoutes"; import "./App.css"; -import BarchartApp from "./components/global_explanation"; -import { - LinechartApp, - FacetKeywordsComp, -} from "./components/local_explanation"; -import SearchContainer from "./components/search_bar"; -import { SEARCHBAR_BOOKS, DUMMY_BOOKS } from "./components/constants"; - -// const DUMMY_BOOKS = [ -// { -// id: 0, -// comic_no: 1665, -// book_title: "X-Men", -// year: 1964, -// genre: "Superhero", -// genre_comb: 1.0, -// supersense: 1.0, -// gender: 1.0, -// panel_ratio: 1.0, -// comic_cover_img: 1.0, -// comic_cover_txt: 1.0, -// }, -// { -// id: 1, -// comic_no: 637, -// book_title: "BatMan", -// year: 1943, -// genre: "Superhero|Detective", -// genre_comb: 1.0, -// supersense: 1.0, -// gender: 1.0, -// panel_ratio: 1.0, -// comic_cover_img: 1.0, -// comic_cover_txt: 1.0, -// }, -// { -// id: 2, -// comic_no: 1661, -// book_title: "Wonder woman", -// year: 1955, -// genre: "Superhero|Action", -// genre_comb: 1.0, -// supersense: 1.0, -// gender: 1.0, -// panel_ratio: 1.0, -// comic_cover_img: 1.0, -// comic_cover_txt: 1.0, -// }, -// { -// id: 3, -// comic_no: 1640, -// book_title: "Tin-Tin", -// year: 1922, -// genre: "Adventure|Detective|Action", -// genre_comb: 1.0, -// supersense: 1.0, -// gender: 1.0, -// panel_ratio: 1.0, -// comic_cover_img: 1.0, -// comic_cover_txt: 1.0, -// }, -// ]; - -const img_folderpath = "../comic_book_covers_ui/"; ///process.env.PUBLIC_URL + Users/surajshashidhar/Downloads/comic_book_covers_ui"; - -const Book = ({ book, onClick, handleQuerySeedClick }) => { - const [image, setImage] = useState(null); - - useEffect(() => { - const retrieveImage = async (book) => { - try { - const response = await axios.get( - `https://api.unsplash.com/search/photos?query=${book.book_title}&client_id=CGhIP6DeLSlR_gRf5_RBQdxTQOzV_tzZOC0A95KFcFU` - ); - const imageUrl = response.data.results[0].urls.regular; - setImage(imageUrl); - } catch (error) { - console.error(error); - } - }; - - const retrieveImageFromLocal = async (book) => { - try { - setImage(img_folderpath + "original_" + book.comic_no + "_1.jpeg"); - } catch (error) { - retrieveImage(book); - console.error(image, error); - } - }; - // console.log("image: ", image); - retrieveImageFromLocal(book); - // console.log("image: ", image); - }, [book]); +export default function App() { + const all_books = []; return (
-
- {image ? ( - {book.book_title} - ) : null} - {" "} -
-
- ); -}; - -function MainPage() { - const [searchValue, setSearchValue] = useState(""); - const [books, setBooks] = useState(DUMMY_BOOKS); - const [clickedBook, setClickedBook] = useState({}); - const [currentWindowClickedBookList, setCurrentWindowClickedBookList] = - useState([]); - const [currentQueryBook, setCurrentQueryBook] = useState(null); - const [filteredBooks, setFilteredBooks] = useState([]); - const [searchText, setSearchText] = useState(""); - const [globalExplanation, setGlobalExplanation] = useState([ - { - genre_comb: 1.0, - supersense: 1.0, - gender: 1.0, - panel_ratio: 1.0, - }, - { - genre_comb: 1.0, - supersense: 1.0, - gender: 1.0, - panel_ratio: 1.0, - }, - ]); - const [searchBarInput, setSearchBarInput] = useState({ - typedQuery: "", - clickedQuery: {}, - }); - const [localExplanation, setLocalExplanation] = useState([ - [3, 5, 3, 2, 8], - [1, 2, 5, 4, 3, 8, 2], - { - Who: [""], - What: [""], - When: [""], - Why: [""], - Where: [""], - How: [""], - }, - { - Who: [""], - What: [""], - When: [""], - Why: [""], - Where: [""], - How: [""], - }, - ["Comic Book Cover"], - ["Comic Book Cover"], - ]); - - const history = useNavigate(); - - const handleSearch = (event) => { - setSearchValue(event.target.value); - }; - - const handleBookClick = (book) => { - setClickedBook(book); - const clicked_book = { ...book }; - clicked_book.clicked = 1.0; - setCurrentWindowClickedBookList([ - ...currentWindowClickedBookList, - clicked_book, - ]); // add clicked books in current window to list, to be used for machine learning in backend - - var localExplanationInfoJSON = { - selected_book_lst: [currentQueryBook, clicked_book], - }; - console.log("localExplanationInfoJSON: ", localExplanationInfoJSON); - - axios - .post( - "http://localhost:8000/local_explanation", - localExplanationInfoJSON, - { - headers: { - Accept: "application/json", - "Content-Type": "application/json;charset=UTF-8", - "Access-Control-Allow-Credentials": true, - }, - } - ) - .then((response) => { - console.log("backend response story pace: ", response); - setLocalExplanation([ - response.data.story_pace[0], - response.data.story_pace[1], - response.data.w5_h1_facets[0], - response.data.w5_h1_facets[1], - response.data.lrp_genre[0], - response.data.lrp_genre[1], - ]); - console.log("New Local Explanation: ", localExplanation); - }) - .catch((error) => { - console.log(error); - }); - }; - - const handleQuerySeedClick = (book) => { - setCurrentQueryBook(book); - - // find uninterested books which were not clicked by user - var unInterestedBookList = books - .filter( - ({ comic_no: id1 }) => - !currentWindowClickedBookList.some(({ comic_no: id2 }) => id2 === id1) - ) - .map((e) => { - e.clicked = 0.0; - return e; - }); - - // remove duplicate clicks - var allBookInteractionInfoList = [...currentWindowClickedBookList]; - var allBookInteractionInfoUniqList = [ - ...new Map( - allBookInteractionInfoList.map((v) => [v.comic_no, v]) - ).values(), - ]; - - // add unlicked and clicked books - var allBookInteractionInfoJSON = { - clicked_book_lst: [ - ...allBookInteractionInfoUniqList, - ...unInterestedBookList, - ], - }; - console.log("all interaction info books ", allBookInteractionInfoJSON); - axios - .post( - "http://localhost:8000/book_search?b_id=" + - book.comic_no + - "&generate_fake_clicks=false", - allBookInteractionInfoJSON, - { - headers: { - Accept: "application/json", - "Content-Type": "application/json;charset=UTF-8", - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": - "PUT, POST, GET, DELETE, PATCH, OPTIONS", - }, - } - ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios - .then((response) => { - // console.log( - // "clicked books in previous window ", - // currentWindowClickedBookList - // ); - setCurrentWindowClickedBookList([book]); // clear the current window and set clicked list as query book - // console.log(response); - // console.log("current clicked book list ", currentWindowClickedBookList); - setBooks(response.data[0]); - setFilteredBooks(response.data[0]); - setGlobalExplanation([globalExplanation[1], response.data[1]]); - console.log("New Global Explanation: ", globalExplanation); - }) - .catch((error) => { - console.log(error); - }); - - // set selected query book as clicked book - handleBookClick(book); - }; - - const handleSearchBarQueryClick = (clickedQuery) => { - var searchBarQuery = { ...clickedQuery }; - axios - .post( - "http://localhost:8000/book_search_with_searchbar_inputs", - searchBarQuery, - { - headers: { - Accept: "application/json", - "Content-Type": "application/json;charset=UTF-8", - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": - "PUT, POST, GET, DELETE, PATCH, OPTIONS", - }, - } - ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios - .then((response) => { - if (searchBarQuery.type === "book") { - setCurrentWindowClickedBookList([ - { - id: searchBarQuery.id, - comic_no: searchBarQuery.comic_no, - book_title: searchBarQuery.book_title, - genre: "", - year: 1950, - genre_comb: 1.0, - supersense: 1.0, - gender: 1.0, - panel_ratio: 1.0, - }, - ]); // clear the current window and set clicked list as query book - } - - // console.log(response); - // console.log("current clicked book list ", currentWindowClickedBookList); - setBooks(response.data[0]); - setFilteredBooks(response.data[0]); - setGlobalExplanation([globalExplanation[1], response.data[1]]); - console.log("New Global Explanation: ", globalExplanation); - }) - .catch((error) => { - console.log(error); - }); - - if (searchBarQuery.type === "book") { - // set selected query book as clicked book - handleBookClick({ - id: searchBarQuery.id, - comic_no: searchBarQuery.comic_no, - book_title: searchBarQuery.book_title, - genre: "", - year: 1950, - genre_comb: 1.0, - supersense: 1.0, - gender: 1.0, - panel_ratio: 1.0, - }); - } - }; - - useEffect(() => { - console.log("useeffect searchBarSelectedData: ", searchBarInput); - var queryBook = searchBarInput.clickedQuery; - handleSearchBarQueryClick(searchBarInput.clickedQuery); - }, [searchBarInput]); - - useEffect(() => { - console.log("state has changed unique, initalized states"); - }, []); - - const ProcessSearchBarInput = (searchBarSelectedData) => { - console.log("received form search bar: ", searchBarInput); - setSearchBarInput(searchBarSelectedData); - - console.log("searchBarInput: ", searchBarInput); - }; - - return ( -
-
-
- -
-
- - {/* -
-

Lorem Ipsum

} - -
- */} -
-
- {books - .filter((book) => { - return ( - book.book_title - .toLowerCase() - .includes(searchValue.toLowerCase()) || - book.genre.toLowerCase().includes(searchValue.toLowerCase()) - ); - }) - .map((book, index) => ( - handleBookClick(book)} - book={book} - handleQuerySeedClick={handleQuerySeedClick} - /> - ))} -
-
-
- {currentQueryBook && ( -

- Current Query Seed: {currentQueryBook.book_title} -{" "} - {currentQueryBook.genre} -

- )} - {clickedBook && ( -

- Clicked Book: {clickedBook.book_title} - {clickedBook.genre} -

- )} -
- -
- -
- -
-
- -
-
- -
-
-
-
+
); } - -export default MainPage; diff --git a/src/App_bkp_20230323.css b/src/App_bkp_20230323.css new file mode 100644 index 0000000..89c5225 --- /dev/null +++ b/src/App_bkp_20230323.css @@ -0,0 +1,285 @@ +.app-container { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + +} + +.nav-bar { + position: absolute; + width: 74%; + left: 0; + top: 0; + display: flex; + justify-content: center; + align-items: flex-start; + background-color: white; + padding: 10px; + height: 10%; + +} + +/* .search-container { + background-color: #E0E0E0; + display: flex; + align-items: center; + width: 50%; + height: 30px; + margin-right: auto; + margin-left: auto; + border: 1px solid gray; + justify-content: center; +} */ + + +/* .search-container input { + width: 100%; +} */ + +.search-container { + display: content; + flex-direction: row; + align-items: center; + justify-content: center; + margin-top: 5px; + width: auto; + margin-right: auto; + margin-left: auto; +} + +.search-container input { + width: 100%; + border:solid black 2px; + border-style: groove; +} + +.book-container { + display: flex; + flex-direction: column; + align-items: end; + justify-content: end; + width: 500px; + height: 400px; + margin: 10px; + border: 10px solid black; + border-radius: 20px; +} + +.book-name { + font-size: 20px; + font-weight: bold; + margin-bottom: 10px; +} + +.book-genre { + font-size: 16px; + margin-bottom: 10px; +} + + +.query-seed-button { + width: 90%; + height: 20px; + background-color: lightblue; + border: 1px solid black; + border-radius: 10px; + font-size: 12px; + font-weight: bold; + cursor: pointer; +} + +.grid-container { + display: grid; + grid-template-columns: repeat(6, 1fr); + grid-template-rows: repeat(3, 1fr); + grid-gap: 2rem; + width: 75%; + +} + +.grid-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2px; + background-color: #fff; + border-radius: 10px; + cursor: pointer; +} + +.global-explanation-container { + padding: 20px; + text-align: center; + height: 400px; + display: flex; +} + +.local-explanation-container { + padding: 20px; + text-align: center; + height: 400px; + display: flex; +} + +.side-bar-container { + background-color: white; + padding: 5px; + width: 25%; + position: absolute; + right: 0; + top: 0; + bottom: 0; + border-left: 3mm ridge; + border-left-style: groove; + border-left-color: black; +} + +.main-screen-old{ + padding-left: 2%; + padding-top: 1%; + margin-top: 7%; + width: 74%; + display: flex; + flex-direction: column; + justify-content:flex-start; + align-items: center; + height: 90%; + padding-top: 10px; /* Adjust as needed */ +} +.main-screen { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-top: 1%; /* Adjust as needed */ + width: 74%; + height: auto; + + margin-left: 1%; + margin-bottom: 1%; + background-color: white; + margin-top: 7%; +} + +.book-object { + width: 73%; + height: 76%; + margin: 10px; + border: 1px #ccc; + border-radius: 5px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.book-object img { + width: 100%; + height: 100%; + align-items: flex-start; + object-fit: cover; + border-radius: 1px 1px 0 0; +} + +.book-object p { + margin: 1px 0; +} + + +/* Added new grid styles */ +.book-grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + grid-auto-rows: repeat(3, 1fr); + gap: 2rem; + margin-left: 5px; + margin-right: 2%; + margin-bottom: 5px; + width: 100%; + height: 100%; + /* background: rgb(2, 0, 36); + background: linear-gradient( + 90deg, + rgba(10, 10, 36, 1) 0%, + rgba(200, 30, 30, 1) 89% + ); */ +} + +@media screen and (max-width: 768px) { + .book-grid { + grid-template-columns: repeat(5, 1fr); + } +} + +.book { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.book img { + display: block; + width: 70%; + height: auto; +} + +.book-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: gainsboro; + opacity: 0; + transition: opacity 1s ease-in-out; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.book:hover .book-overlay { + opacity: 0.9; + transition-delay: 1s; +} + +.book-overlay h2 { + font-size: 1rem; + margin-bottom: 0.5rem; + color: black; +} + +.book-overlay p { + font-size: 1.0rem; + color: black; +} + +.link { + width: 100%; + height: 100%; +} + +.book-card { + object-fit: cover; + width: auto; + height: auto; + position: relative; +} + +.imageLarge { + margin: 1em 5px 17px 0; + display: block; + width: 70%; + height: auto; + transition: transform 450ms; + border-radius: 5px; + cursor: pointer; +} + +.imageLarge:hover { + transform: scale(1.08); +} diff --git a/src/App_bkp_20230323.js b/src/App_bkp_20230323.js new file mode 100644 index 0000000..cefe0a2 --- /dev/null +++ b/src/App_bkp_20230323.js @@ -0,0 +1,414 @@ +import { useNavigate } from "react-router-dom"; +import axios from "axios"; +import "./App.css"; +import BarchartApp from "./components/global_explanation"; +import { + LinechartApp, + FacetKeywordsComp, +} from "./components/local_explanation"; +import SearchContainer from "./components/search_bar"; +import { + SEARCHBAR_BOOKS, + DUMMY_BOOKS, + DEFAULT_LOCAL_EXPLANATION, + FACET_KEYS, +} from "./components/constants"; +import React, { useState, useRef, useEffect } from "react"; +import book_card_styles from "./components/book_card.css"; + +const img_folderpath = "../comic_book_covers_ui/"; ///process.env.PUBLIC_URL + Users/surajshashidhar/Downloads/comic_book_covers_ui"; + +const Book = ({ book, onClick, handleQuerySeedClick }) => { + const [image, setImage] = useState(null); + + useEffect(() => { + const retrieveImage = async (book) => { + try { + const response = await axios.get( + `https://api.unsplash.com/search/photos?query=${book.book_title}&client_id=CGhIP6DeLSlR_gRf5_RBQdxTQOzV_tzZOC0A95KFcFU` + ); + const imageUrl = response.data.results[0].urls.regular; + setImage(imageUrl); + } catch (error) { + console.error(error); + } + }; + + const retrieveImageFromLocal = async (book) => { + try { + setImage(img_folderpath + "original_" + book.comic_no + "_1.jpeg"); + } catch (error) { + retrieveImage(book); + console.error(image, error); + } + }; + // console.log("image: ", image); + retrieveImageFromLocal(book); + // console.log("image: ", image); + }, [book]); + + return ( +
+
+ {image ? ( + {book.book_title} + ) : null} + {" "} +
+
+ ); +}; + +const BookCard = ({ book }) => { + // image state + const [image, setImage] = useState(null); + + // set image + useEffect(() => { + const retrieveImageFromLocal = async (book) => { + try { + setImage(img_folderpath + "original_" + book.comic_no + "_1.jpeg"); + } catch (error) { + console.error(image, error); + } + }; + retrieveImageFromLocal(book); + }, [book]); + + return ( +
+ {image ? {book.book_title} : null} +
+ ); +}; + +function MainPage() { + const [searchValue, setSearchValue] = useState(""); + const [books, setBooks] = useState(DUMMY_BOOKS); + const [currentQueryBook, setCurrentQueryBook] = useState(null); + const [filteredBooks, setFilteredBooks] = useState([]); + const [searchText, setSearchText] = useState(""); + const [globalExplanation, setGlobalExplanation] = useState([ + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + }, + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + }, + ]); + const [searchBarInput, setSearchBarInput] = useState({ + typedQuery: "", + clickedQuery: {}, + }); + const [localExplanation, setLocalExplanation] = useState( + DEFAULT_LOCAL_EXPLANATION + ); + + // new states using hover + // handle hovering or interested books + const [hoveredBook, setHoveredBook] = useState(null); + // for delaying hovering action, delay of 1 second before capturing hover + const [showOverlay, setShowOverlay] = useState(false); + const timeoutRef = useRef(null); + const [hoveredBookList, setHoveredBookList] = useState([]); + + const handleMouseEnter = (book) => { + timeoutRef.current = setTimeout(() => { + setShowOverlay(true); + setHoveredBook(book); + setHoveredBookList((prevList) => [...prevList, book]); + console.log("hovered book: ", book); + console.log("hovered book list: ", hoveredBookList.length); + + // show local explanations + var localExplanationInfoJSON = { + selected_book_lst: [currentQueryBook, book], + }; + console.log("localExplanationInfoJSON: ", localExplanationInfoJSON); + + axios + .post( + "http://localhost:8000/local_explanation", + localExplanationInfoJSON, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + }, + } + ) + .then((response) => { + console.log("backend response story pace: ", response); + setLocalExplanation([ + response.data.story_pace[0], + response.data.story_pace[1], + response.data.w5_h1_facets[0], + response.data.w5_h1_facets[1], + response.data.lrp_genre[0], + response.data.lrp_genre[1], + ]); + console.log("New Local Explanation: ", localExplanation); + }) + .catch((error) => { + console.log(error); + }); + }, 1000); + }; + + const handleMouseLeave = () => { + clearTimeout(timeoutRef.current); + setShowOverlay(false); + setHoveredBook(null); + setLocalExplanation(DEFAULT_LOCAL_EXPLANATION); + }; + + const history = useNavigate(); + + const handleSearch = (event) => { + setSearchValue(event.target.value); + }; + + const handleClick = (book) => { + // issue for query only if it is not already query book + if (!book.query_book) { + // set selected query book as clicked book + setCurrentQueryBook(book); + + // find uninterested books which were not hovered by user + var unInterestedBookList = books + .filter( + ({ comic_no: id1 }) => + !hoveredBookList.some(({ comic_no: id2 }) => id2 === id1) + ) + .map((e) => { + e.interested = 0.0; + return e; + }); + + // remove duplicate clicks + var allBookInteractionInfoList = [ + ...hoveredBookList.map((e) => { + e.interested = 1.0; + return e; + }), + ]; + var allBookInteractionInfoUniqList = [ + ...new Map( + allBookInteractionInfoList.map((v) => [v.comic_no, v]) + ).values(), + ]; + + // add uninterested and interested books + var allBookInteractionInfoJSON = { + interested_book_lst: [ + ...allBookInteractionInfoUniqList, + ...unInterestedBookList, + ], + }; + console.log("all interaction info books ", allBookInteractionInfoJSON); + axios + .post( + "http://localhost:8000/book_search?b_id=" + + book.comic_no + + "&generate_fake_clicks=false", + allBookInteractionInfoJSON, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": + "PUT, POST, GET, DELETE, PATCH, OPTIONS", + }, + } + ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios + .then((response) => { + setHoveredBookList([]); // clear the current window and set clicked list as query book + setBooks(response.data[0]); + setFilteredBooks(response.data[0]); + setGlobalExplanation([globalExplanation[1], response.data[1]]); + console.log("New Global Explanation: ", globalExplanation); + }) + .catch((error) => { + console.log(error); + }); + } + }; + + const handleSearchBarQueryClick = (clickedQuery) => { + var searchBarQuery = { ...clickedQuery }; + axios + .post( + "http://localhost:8000/book_search_with_searchbar_inputs", + searchBarQuery, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": + "PUT, POST, GET, DELETE, PATCH, OPTIONS", + }, + } + ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios + .then((response) => { + if (searchBarQuery.type === "book") { + setHoveredBookList([ + { + id: searchBarQuery.id, + comic_no: searchBarQuery.comic_no, + book_title: searchBarQuery.book_title, + genre: "", + year: 1950, + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + }, + ]); // clear the current window and set clicked list as query book + } + + // console.log(response); + // console.log("current clicked book list ", currentWindowClickedBookList); + setBooks(response.data[0]); + setFilteredBooks(response.data[0]); + setGlobalExplanation([globalExplanation[1], response.data[1]]); + console.log("New Global Explanation: ", globalExplanation); + }) + .catch((error) => { + console.log(error); + }); + + if (searchBarQuery.type === "book") { + // set selected query book as clicked book + setCurrentQueryBook({ + id: searchBarQuery.id, + comic_no: searchBarQuery.comic_no, + book_title: searchBarQuery.book_title, + genre: "", + year: 1950, + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + }); + } + }; + + useEffect(() => { + console.log("useeffect searchBarSelectedData: ", searchBarInput); + var queryBook = searchBarInput.clickedQuery; + handleSearchBarQueryClick(searchBarInput.clickedQuery); + }, [searchBarInput]); + + useEffect(() => { + console.log("state has changed unique, initalized states"); + }, []); + + const ProcessSearchBarInput = (searchBarSelectedData) => { + console.log("received form search bar: ", searchBarInput); + setSearchBarInput(searchBarSelectedData); + + console.log("searchBarInput: ", searchBarInput); + }; + + const retrieveImageFromLocal = async (book) => { + try { + return img_folderpath + "original_" + book.comic_no + "_1.jpeg"; + } catch (error) { + console.error(book, error); + } + }; + + return ( +
+
+
+ +
+
+
+
+ {books.map((book) => ( +
handleMouseEnter(book)} + onMouseLeave={() => handleMouseLeave()} + onClick={() => handleClick(book)} + style={ + currentQueryBook === book.id ? { border: "3px solid blue" } : {} + } + > + {book.book_title} + {hoveredBook === book && showOverlay && ( +
+

genre - {book.genre.split("|").join(", ")}

+

+ {FACET_KEYS.map( + (key) => + Array.from(localExplanation[3][key]) && + Array.from(localExplanation[3][key]) + .slice(0, 2) + .join(", ") + ", " + )} +

+
+ )} +
+ ))} +
+
+
+ {currentQueryBook && ( +

+ Current Query Seed: {currentQueryBook.book_title} -{" "} + {currentQueryBook.genre} +

+ )} + {hoveredBook && ( +

+ Clicked Book: {hoveredBook.book_title} - {hoveredBook.genre} +

+ )} +
+ +
+ +
+ +
+ + {/*
+ +
*/} +
+
+
+
+ ); +} + +export default MainPage; diff --git a/src/backend_api_calls/FetchComicPDF.js b/src/backend_api_calls/FetchComicPDF.js new file mode 100644 index 0000000..feaf72f --- /dev/null +++ b/src/backend_api_calls/FetchComicPDF.js @@ -0,0 +1,24 @@ +import axios from "axios"; +import { SYSTEMS_TO_API_ENDPOINT_MAPPING } from "../components/constants"; + +function FetchComicPDF(viewBook) { + console.log("viewing book : ", viewBook); + var pdfUrl = null; + var system_type = JSON.parse(sessionStorage.getItem("system_type")); + var endpointAPI = SYSTEMS_TO_API_ENDPOINT_MAPPING[system_type]["view_pdf"]; + return new Promise((resolve, reject) => { + axios + .get(`${endpointAPI}/${viewBook.comic_no}`, { + responseType: "arraybuffer", + }) + .then((response) => { + resolve(response); + }) + .catch((error) => { + console.log("error in viewing pdf: ", error); + reject(pdfUrl); + }); + }); +} + +export default FetchComicPDF; diff --git a/src/backend_api_calls/FetchCompareExplanations.js b/src/backend_api_calls/FetchCompareExplanations.js new file mode 100644 index 0000000..3fff784 --- /dev/null +++ b/src/backend_api_calls/FetchCompareExplanations.js @@ -0,0 +1,39 @@ +import axios from "axios"; +import { SYSTEMS_TO_API_ENDPOINT_MAPPING } from "../components/constants"; + +function FetchCompareExplanations(compareBooksCheckedList) { + console.log("comparision books : ", compareBooksCheckedList); + var comparedExplanations = null; + var system_type = JSON.parse(sessionStorage.getItem("system_type")); + var endpointAPI = SYSTEMS_TO_API_ENDPOINT_MAPPING[system_type]["comparision"]; + return new Promise((resolve, reject) => { + axios + .post( + `${endpointAPI}`, + { + selected_book_lst: compareBooksCheckedList, + }, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": + "PUT, POST, GET, DELETE, PATCH, OPTIONS", + }, + } + ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios + .then((response) => { + comparedExplanations = response.data; + console.log("compared explanations : ", comparedExplanations); + resolve(response); + }) + .catch((error) => { + console.log("error in comparing explanations: ", error); + reject(comparedExplanations); + }); + }); +} + +export default FetchCompareExplanations; diff --git a/src/backend_api_calls/FetchLocalExplanations.js b/src/backend_api_calls/FetchLocalExplanations.js new file mode 100644 index 0000000..9ea12b5 --- /dev/null +++ b/src/backend_api_calls/FetchLocalExplanations.js @@ -0,0 +1,39 @@ +import axios from "axios"; +import { SYSTEMS_TO_API_ENDPOINT_MAPPING } from "../components/constants"; + +function FetchLocalExplanations(localExplanationInfoJSON) { + console.log("localExplanationInfoJSON: ", localExplanationInfoJSON); + var localExplanation = []; + var system_type = JSON.parse(sessionStorage.getItem("system_type")); + var endpointAPI = + SYSTEMS_TO_API_ENDPOINT_MAPPING[system_type]["local_explanation"]; + return new Promise((resolve, reject) => { + axios + .post(`${endpointAPI}`, localExplanationInfoJSON, { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + }, + }) + .then((response) => { + console.log("backend response story pace: ", response); + localExplanation = [ + response.data.story_pace[0], + response.data.story_pace[1], + response.data.w5_h1_facets[0], + response.data.w5_h1_facets[1], + response.data.lrp_genre[0], + response.data.lrp_genre[1], + ]; + console.log("New Local Explanation: ", localExplanation); + resolve(localExplanation); + }) + .catch((error) => { + console.log(error); + reject(localExplanation); + }); + }); +} + +export default FetchLocalExplanations; diff --git a/src/backend_api_calls/FetchSearchResultsForBookGrid.js b/src/backend_api_calls/FetchSearchResultsForBookGrid.js new file mode 100644 index 0000000..48bc480 --- /dev/null +++ b/src/backend_api_calls/FetchSearchResultsForBookGrid.js @@ -0,0 +1,69 @@ +import axios from "axios"; +import { + SEARCHBAR_BOOKS, + DUMMY_BOOKS, + DEFAULT_LOCAL_EXPLANATION, + FACET_KEYS, + SYSTEMS_TO_API_ENDPOINT_MAPPING, +} from "../components/constants"; + +function FetchSearchResultsforBookGrid( + comic_no, + generate_fake_clicks, + allBookInteractionInfoJSON, + userFacetWeights +) { + console.log( + "input parameters: ", + comic_no, + generate_fake_clicks, + allBookInteractionInfoJSON, + userFacetWeights + ); + console.log( + "possible link : http://localhost:8000/book_search?b_id=" + + comic_no + + "&generate_fake_clicks=" + + generate_fake_clicks + ); + console.log("here"); + var searchResults = DUMMY_BOOKS; + + var system_type = JSON.parse(sessionStorage.getItem("system_type")); + var endpointAPI = SYSTEMS_TO_API_ENDPOINT_MAPPING[system_type]["book_grid"]; + + return new Promise((resolve, reject) => { + axios + .post( + `${endpointAPI}` + + parseInt(comic_no) + + "&generate_fake_clicks=" + + generate_fake_clicks, + { + cbl: allBookInteractionInfoJSON, + input_feature_importance_dict: userFacetWeights, + }, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": + "PUT, POST, GET, DELETE, PATCH, OPTIONS", + }, + } + ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios + .then((response) => { + searchResults = [...response.data[0]]; + console.log("response from api: ", searchResults); + resolve(response); + }) + .catch((error) => { + console.log("error in landing page to grid search: ", error); + reject(searchResults); + }); + }); +} + +export default FetchSearchResultsforBookGrid; diff --git a/src/backend_api_calls/FetchSearchResultsforSearchbarQuery.js b/src/backend_api_calls/FetchSearchResultsforSearchbarQuery.js new file mode 100644 index 0000000..12c7a11 --- /dev/null +++ b/src/backend_api_calls/FetchSearchResultsforSearchbarQuery.js @@ -0,0 +1,55 @@ +import axios from "axios"; +import { + SEARCHBAR_BOOKS, + DUMMY_BOOKS, + DEFAULT_LOCAL_EXPLANATION, + FACET_KEYS, + SYSTEMS_TO_API_ENDPOINT_MAPPING, +} from "../components/constants"; + +function FetchSearchResultsforSearchbarQuery( + clickedQuery, + isEditable, + userFacetWeights +) { + var searchBarQuery = { ...clickedQuery.clickedBook }; + console.log("clicked query inside fetch: ", searchBarQuery); + var searchResults = DUMMY_BOOKS; + + var system_type = JSON.parse(sessionStorage.getItem("system_type")); + var endpointAPI = SYSTEMS_TO_API_ENDPOINT_MAPPING[system_type]["search_bar"]; + + return new Promise((resolve, reject) => { + axios + .post( + `${endpointAPI}`, + { + searchbar_query: searchBarQuery, + generate_fake_clicks: isEditable, + input_feature_importance_dict: userFacetWeights, + }, + { + headers: { + Accept: "application/json", + "Content-Type": "application/json;charset=UTF-8", + "Access-Control-Allow-Credentials": true, + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": + "PUT, POST, GET, DELETE, PATCH, OPTIONS", + }, + } + ) // add headers for cors - https://stackoverflow.com/questions/45975135/access-control-origin-header-error-using-axios + .then((response) => { + searchResults = [...response.data[0]]; + console.log("response SERP from api: ", searchResults); + console.log("all response: ", response); + resolve(response); + }) + .catch((error) => { + console.log("error in landing page to grid search: ", error); + reject(searchResults); + }); + }); +} + +export default FetchSearchResultsforSearchbarQuery; diff --git a/src/components/CompareBooks.css b/src/components/CompareBooks.css new file mode 100644 index 0000000..c3c8562 --- /dev/null +++ b/src/components/CompareBooks.css @@ -0,0 +1,35 @@ +/* .cross-tab { + border-collapse: collapse; + width: 100%; +} + +.cross-tab th, +.cross-tab td { + border: 1px solid #ccc; + padding: 8px; + text-align: center; +} + +.cross-tab th { + background-color: #f2f2f2; +} + +.cross-tab tbody tr:nth-child(2n) { + background-color: #f2f2f2; +} */ + +.cross-tab { + border-collapse: collapse; + width: 100%; +} + +.cross-tab th, +.cross-tab td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; +} + +.cross-tab th { + background-color: #f2f2f2; +} diff --git a/src/components/CompareBooks.js b/src/components/CompareBooks.js new file mode 100644 index 0000000..40b218e --- /dev/null +++ b/src/components/CompareBooks.js @@ -0,0 +1,179 @@ +import React from "react"; +import "./CompareBooks.css"; +import { Chip } from "@mui/material"; + +/* +const CrossTab = ({ comparedBooks }) => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + {comparedBooks.compared_books.map((book) => { + const bookNumber = Object.keys(book)[0]; + const { genres, story_pace, characters } = book[bookNumber]; + + return ( + + + + + + + + + + + + + + + + + + + + + ); + })} + +
Genre TypesStory PaceCharacters
Main CategoriesTotal PagesMale Characters
Total PanelsFemale Characters
Pace NumberOccupations
Book {bookNumber}{genres.genre.join(", ")}{story_pace.num_pages}{characters.male_characters.join(", ")}
{story_pace.num_panels}{characters.female_characters.join(", ")}
{story_pace.pace_of_story.toFixed(2)}{characters.occupations.join(", ")}
+ ); +}; + +export default CrossTab; +*/ + +const img_folderpath = "../../comic_book_covers_ui/"; +const subCategoriesWithLists = [ + "genre", + "male_characters", + "female_characters", + "occupations", +]; +function ChipsList({ list }) { + return
{list && list.map((val) => )}
; +} + +const CompareBooks = ({ data }) => { + console.log("inside compare books: ", data.compared_books); + const books = + data.compared_books && + data.compared_books.map((item) => Object.keys(item)[0]); + const categories = ["genres", "story_pace", "characters"]; + const subCategories = { + story_pace: ["num_pages", "num_panels", "pace_of_story"], + characters: [ + "total_characters", + "male_characters", + "female_characters", + "occupations", + ], + genres: ["genre"], + }; + + const getCellValue = (book, category, subCategory) => { + const item = data.compared_books.find( + (item) => Object.keys(item)[0] === book + )[book][category]; + + console.log("item for : ", item); + console.log("category: ", category); + console.log("subcategory: ", subCategory); + if (subCategory && subCategoriesWithLists.includes(subCategory) && item) { + console.log("detected list: ", item.category); + console.log("chips outout: ", ChipsList(item[subCategory])); + return item[subCategory].join(", "); // ChipsList(item[subCategory]) + } else if (subCategory && !subCategoriesWithLists.includes(subCategory)) { + return item[subCategory] || "-"; + } + if (Array.isArray(item)) { + console.log("detected list: ", item); + console.log("chips outout: ", ChipsList(item)); + return ChipsList(item); + } + + return item || "-"; + }; + + return ( + + + + + {categories.map((category) => + subCategories[category] ? ( + + ) : ( + + ) + )} + + + + {categories.map((category) => + subCategories[category] + ? subCategories[category].map((subCategory) => ( + + )) + : null + )} + + + + {books && + books.map((book) => ( + + + {categories.map((category) => + subCategories[category] ? ( + subCategories[category].map((subCategory) => ( + + )) + ) : ( + + ) + )} + + ))} + +
Books + {category} + {category}
{subCategory}
+ {"no + + {getCellValue(book, category, subCategory)} + + {getCellValue(book, category)} +
+ ); +}; + +export default CompareBooks; diff --git a/src/components/book_card.css b/src/components/book_card.css new file mode 100644 index 0000000..c61663c --- /dev/null +++ b/src/components/book_card.css @@ -0,0 +1,74 @@ +.book-card { + position: center; + width: auto; + height: auto; + margin: 1px; + border: auto solid #ccc; + border-radius: 1px; + overflow: hidden; + cursor: pointer; +} + +.book-card:hover { + border-color: green; +} + +.book-card.clicked { + border-color: blue; +} + +.book-card.clicked:hover { + border-color: blue; +} + +.book-card img { + display: block; + width: 75%; + height: 75%; +} + +.card-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + color: white; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 1px; + transition: opacity 1s ease-in-out; + opacity: 0.8; +} + +.card-overlay ul { + list-style: none; + padding: 0; + margin: 0; +} + +.card-overlay li { + margin: 5px; +} + +.card-overlay h2 { + font-size: 0.6rem; + margin-bottom: 0.4rem; +} + +.card-overlay p { + font-size: 0.3rem; + color: gray; +} + +.book-card.hovered .card-overlay { + opacity: 1; + transition-delay: 1s; +} + +.book-card.hovered img { + filter: grayscale(100%); +} diff --git a/src/components/book_card.js b/src/components/book_card.js new file mode 100644 index 0000000..9db2f56 --- /dev/null +++ b/src/components/book_card.js @@ -0,0 +1,62 @@ +import React, { useState, useRef, useEffect } from "react"; +import "./book_card.css"; + +const BookCard = ({ book, queryUsingBook, appendToInterestedBookList }) => { + // handle hovering or interested books + const [hoveredBook, setHoveredBook] = useState(null); + // for delaying hovering action, delay of 1 second before capturing hover + const [showOverlay, setShowOverlay] = useState(false); + const timeoutRef = useRef(null); + + const [hoveredBookList, setHoveredBookList] = useState([]); + + const handleMouseEnter = (book) => { + timeoutRef.current = setTimeout(() => { + setShowOverlay(true); + setHoveredBook(book); + setHoveredBookList((prevList) => [...prevList, book]); + console.log("hovered book: ", book); + console.log("hovered book list: ", hoveredBookList.length); + }, 1000); + }; + + const handleMouseLeave = () => { + clearTimeout(timeoutRef.current); + setShowOverlay(false); + setHoveredBook(null); + appendToInterestedBookList(book); + }; + // useEffect(() => console.log("xlist: ", hoveredBookList), [hoveredBookList]); + + // handle clicks + const [clicked, setClicked] = useState(false); + const [clickedBook, setClickedBook] = useState(null); + + const handleClick = (book) => { + setClicked(!clicked); + setClickedBook(clicked ? null : book.id); + console.log("clicked book: ", book); + queryUsingBook(book, hoveredBookList); + }; + + return ( +
handleMouseEnter(book)} + onMouseLeave={() => handleMouseLeave()} + onClick={() => handleClick(book)} + style={clickedBook === book.id ? { border: "3px solid blue" } : {}} + > + {book.name} + {hoveredBook === book && showOverlay && ( +
+

{book.name}

+

{book.genre.split("|").join(", ")}

+
+ )} +
+ ); +}; + +export default BookCard; diff --git a/src/components/constants.js b/src/components/constants.js index e1954af..06a3d31 100644 --- a/src/components/constants.js +++ b/src/components/constants.js @@ -24057,8 +24057,9 @@ export const DUMMY_BOOKS = [ panel_ratio: 1.0, comic_cover_img: 1.0, comic_cover_txt: 1.0, + thumbsUp: 0, + thumbsDown: 0, }, - , { id: 1, comic_no: 637, @@ -24071,8 +24072,9 @@ export const DUMMY_BOOKS = [ panel_ratio: 1.0, comic_cover_img: 1.0, comic_cover_txt: 1.0, + thumbsUp: 0, + thumbsDown: 0, }, - , { id: 2, comic_no: 1661, @@ -24085,8 +24087,10 @@ export const DUMMY_BOOKS = [ panel_ratio: 1.0, comic_cover_img: 1.0, comic_cover_txt: 1.0, + thumbsUp: 0, + thumbsDown: 0, }, - , + { id: 3, comic_no: 1640, @@ -24099,6 +24103,227 @@ export const DUMMY_BOOKS = [ panel_ratio: 1.0, comic_cover_img: 1.0, comic_cover_txt: 1.0, + thumbsUp: 0, + thumbsDown: 0, + }, +]; + +export const DEFAULT_LOCAL_EXPLANATION = [ + [0], + [0], + { + Who: [""], + What: [""], + When: [""], + Why: [""], + Where: [""], + How: [""], + }, + { + Who: [""], + What: [""], + When: [""], + Why: [""], + Where: [""], + How: [""], + }, + [""], + [""], +]; + +export const FACET_KEYS = ["Who", "What", "When", "Why", "Where", "How"]; + +export const FAMOUS_TITLES_LIST = [ + { + id: 542, + comic_no: 542, + book_title: "01- Asterix the Gaul", + backdrop_path: "../../comic_book_covers_ui/original_542_1.jpeg", + genre: "humor", + year: 1950, + }, + { + id: 525, + comic_no: 525, + book_title: "Archie 75 Series 001 - Archie (2015) (Digital-Empire)", + backdrop_path: "../../comic_book_covers_ui/original_525_1.jpeg", + genre: "teen|romance|humor", + year: 1950, + }, + { + id: 564, + comic_no: 564, + book_title: "Avengers001", + backdrop_path: "../../comic_book_covers_ui/original_564_1.jpeg", + genre: "superhero", + year: 1950, + }, + { + id: 864, + comic_no: 864, + book_title: "Fantastic_Four001", + backdrop_path: "../../comic_book_covers_ui/original_864_1.jpeg", + genre: "superhero", + year: 1950, + }, + { + id: 1260, + comic_no: 1260, + book_title: + "Justice League of America - The Silver Age v01 (2016) (digital) (Son of Ultron-Empire)", + backdrop_path: "../../comic_book_covers_ui/original_1260_1.jpeg", + genre: "superhero|action", + year: 1950, }, - , ]; + +export const CLASSICS_LIST = [ + { + id: 12, + comic_no: 12, + book_title: "Brenda Starr - Silver lining in sun valley", + backdrop_path: "../../comic_book_covers_ui/original_12_1.jpeg", + genre: "detective|female", + year: 1950, + }, + { + id: 34, + comic_no: 34, + book_title: "Cowgirl Romances - Ride Fast for Wyoming", + backdrop_path: "../../comic_book_covers_ui/original_34_1.jpeg", + genre: "western|romance", + year: 1950, + }, + { + id: 51, + comic_no: 51, + book_title: "Crack Western - Mexican Massacare", + backdrop_path: "../../comic_book_covers_ui/original_51_1.jpeg", + genre: "western|adventure", + year: 1950, + }, + { + id: 80, + comic_no: 80, + book_title: "Famous Funnies - 24", + backdrop_path: "../../comic_book_covers_ui/original_80_1.jpeg", + genre: "humor", + year: 1950, + }, + { + id: 152, + comic_no: 152, + book_title: "Jumbo Comics - Sheena - Pantheras of kajoo kazar", + backdrop_path: "../../comic_book_covers_ui/original_152_1.jpeg", + genre: "humor", + year: 1950, + }, +]; + +export const NEW_ISSUES_LIST = [ + { + id: 1073, + comic_no: 1073, + book_title: "From Hell - Master Edition 001 (2018) (Digital) (Zone-Empire)", + backdrop_path: "../../comic_book_covers_ui/original_1073_1.jpeg", + genre: "crime horror horror", + year: 1990, + }, + { + id: 1300, + comic_no: 1300, + book_title: "Persepolis (2005)", + backdrop_path: "../../comic_book_covers_ui/original_1300_1.jpeg", + genre: "history horror", + year: 2005, + }, + { + id: 1595, + comic_no: 1595, + book_title: "The Boys - Omnibus v01", + backdrop_path: "../../comic_book_covers_ui/original_1595_1.jpeg", + genre: "superhero horror fanatasy", + year: 2019, + }, + { + id: 1653, + comic_no: 1653, + book_title: "Tomb Raider 001", + backdrop_path: "../../comic_book_covers_ui/original_1653_1.jpeg", + genre: "female adventure", + year: 2000, + }, + { + id: 1660, + comic_no: 1660, + book_title: "Watchmen", + backdrop_path: "../../comic_book_covers_ui/original_1660_1.jpeg", + genre: "dark fanatasy", + year: 1950, + }, +]; + +export const delay = (ms) => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; + +/* +Wayne - System with personalization, reranking, comparision, relevance feedback explanation +Stark - System with personalization, reranking, random comparision, relevance feedback explanation +Croft - System with personalization, reranking, comparision, random relevance feedback explanation +Butcher - System with no personalization, no reranking, comparision, relevance feedback explanation +Gray - System with random personalization, random reranking, comparision, relevance feedback explanation + +RQ1 - Wayne vs Stark +RQ2 - Wayne vs Croft +RQ3 - Wayne vs Butcher vs Gray +*/ + +export const SYSTEMS_TO_API_ENDPOINT_MAPPING = { + Wayne: { + search_bar: "http://localhost:8000/book_search_with_searchbar_inputs", + book_grid: "http://localhost:8000/book_search?b_id=", + local_explanation: "http://localhost:8000/local_explanation", + comparision: "http://localhost:8000/compare_books", + relevance_feedback: "", + view_pdf: "http://localhost:8000/view_comic_book", + }, + Stark: { + search_bar: "http://localhost:8000/book_search_with_searchbar_inputs", + book_grid: "http://localhost:8000/book_search?b_id=", + local_explanation: "http://localhost:8000/local_explanation", + comparision: "http://localhost:8000/compare_books_with_random", + relevance_feedback: "", + view_pdf: "http://localhost:8000/view_comic_book/", + }, + Croft: { + search_bar: "http://localhost:8000/book_search_with_searchbar_inputs", + book_grid: + "http://localhost:8000/book_search_with_random_explanation_feedback?b_id=", + local_explanation: "http://localhost:8000/local_explanation", + comparision: "http://localhost:8000/compare_books", + relevance_feedback: "", + view_pdf: "http://localhost:8000/view_comic_book/", + }, + Butcher: { + search_bar: "http://localhost:8000/book_search_with_searchbar_inputs", + book_grid: + "http://localhost:8000/book_search_with_no_personalization?b_id=", + local_explanation: "http://localhost:8000/local_explanation", + comparision: "http://localhost:8000/compare_books", + relevance_feedback: "", + view_pdf: "http://localhost:8000/view_comic_book/", + }, + Gray: { + search_bar: + "http://localhost:8000/book_search_with_searchbar_inputs_and_random_serp", + book_grid: + "http://localhost:8000/book_search_with_random_serp_results?b_id=", + local_explanation: "http://localhost:8000/local_explanation", + comparision: "http://localhost:8000/compare_books", + relevance_feedback: "", + view_pdf: "http://localhost:8000/view_comic_book/", + }, +}; diff --git a/src/components/global_explanation_relevance_feedback.css b/src/components/global_explanation_relevance_feedback.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/global_explanation_relevance_feedback.js b/src/components/global_explanation_relevance_feedback.js new file mode 100644 index 0000000..4ac0766 --- /dev/null +++ b/src/components/global_explanation_relevance_feedback.js @@ -0,0 +1,91 @@ +import React from "react"; +import { makeStyles } from "@material-ui/core/styles"; +import Chip from "@material-ui/core/Chip"; +import Tooltip from "@material-ui/core/Tooltip"; + +const initial_chips = [ + { + comic_no: 0, + book_title: "Blue Bolt", + query_book: false, + explanation_lst: ["Angular", "Black and Blue", "Yellowed"], + }, + { + comic_no: 521, + book_title: "Tin Tin", + query_book: false, + explanation_lst: ["steamship themed", "Mummy Coffin", "Pharoahs and Egypt"], + }, + { + comic_no: 551, + book_title: "Asterix", + query_book: false, + explanation_lst: [ + "Roman clothes", + "Golden and Blue", + "Coloseium", + "No Themes Found", + ], + }, +]; + +const useStyles = makeStyles((theme) => ({ + chip: { + margin: theme.spacing(0.5), + }, + header: { + margin: theme.spacing(1, 0), + }, +})); + +function ExplanationChips(props) { + const books = props.inputData || initial_chips; + const classes = useStyles(); + + const queryBooks = books.filter((book) => book.query_book); + const nonQueryBooks = books.filter((book) => !book.query_book); + + const allBookTitles = books.map((book) => book.book_title); + + return ( +
+ {queryBooks.length > 0 ? ( +

+ Similar themes between current search results and your previous + interested books{" "} + {allBookTitles.map((title, index) => ( + + {title} + {index !== allBookTitles.length - 1 ? ", " : ""} + + ))} +

+ ) : ( +

+ Similar themes between current search results and your previous + interested books{" "} + {allBookTitles.map((title, index) => ( + + {title} + {index !== allBookTitles.length - 1 ? ", " : ""} + + ))} +

+ )} + {books.map((book) => + book.explanation_lst.map((explanation, index) => ( + + + + )) + )} +
+ ); +} + +export default ExplanationChips; diff --git a/src/components/global_explanation_relevance_feedback_new.js b/src/components/global_explanation_relevance_feedback_new.js new file mode 100644 index 0000000..4d0f629 --- /dev/null +++ b/src/components/global_explanation_relevance_feedback_new.js @@ -0,0 +1,201 @@ +import { useEffect, useState } from "react"; +import { Box, Chip } from "@mui/material"; +import { makeStyles } from "@material-ui/core/styles"; +import Tooltip from "@material-ui/core/Tooltip"; + +const img_folderpath = "../../comic_book_covers_ui/"; + +// Example usage +var cdata = [ + { + comic_no: 407, + book_title: "Swift Arrow: Test of the Tomahawk", + genre: "western|action", + year: 1950, + query_book: false, + explanation_lst: [ + [ + "a native american warrior", + "bronze - skinned", + "20.jpeg", + "Captain Marvel Jr - The hunt for treasure", + ], + [ + "with a parrot on his shoulder", + "hes wearing a red neckerchief", + "20.jpeg", + "Captain Marvel Jr - The hunt for treasure", + ], + ], + }, + { + comic_no: 23, + book_title: "Captain Marvel Jr - Keep the Liberty Bell Ringing", + genre: "superhero|humour", + year: 1950, + query_book: false, + explanation_lst: [ + [ + "comic artstyle", + "a cover of a blue bolt comic book", + "11.jpeg", + "Boy - Detective: Death Trap", + ], + [ + "black cape", + "cobalt blue", + "20.jpeg", + "Captain Marvel Jr - The hunt for treasure", + ], + ], + }, + { + comic_no: 469, + book_title: "Wild Bill Pecos: Thunderbird", + genre: "western", + year: 1950, + query_book: false, + explanation_lst: [ + [ + "western comic art", + "a cover of a blue bolt comic book", + "11.jpeg", + "Boy - Detective: Death Trap", + ], + [ + "a comic book with a cowboy on a horse", + "a cover of a blue bolt comic book", + "11.jpeg", + "Boy - Detective: Death Trap", + ], + ], + }, +]; + +const useStyles = makeStyles((theme) => ({ + root: { + display: "flex", + flexDirection: "row", + gap: theme.spacing(1), + alignItems: "center", + justifyContent: "center", + flexWrap: "wrap", + marginBottom: theme.spacing(2), + }, + chip: { + cursor: "pointer", + position: "relative", + }, + box: { + position: "relative", + top: 0, + left: "1%", + + backgroundColor: "white", + width: "50%", + padding: theme.spacing(1), + borderRadius: "4px", + boxShadow: "0 4px 4px rgba(0, 0, 0, 0.15)", + textAlign: "center", + }, +})); + +function ChipsWithBox({ data }) { + const classes = useStyles(); + const [isBoxVisible, setIsBoxVisible] = useState(false); + const [clickedChipID, setClickedChipID] = useState(null); + + const handleChipClick = (comicNo, index) => { + if (isBoxVisible) { + console.log("clicked to leave"); + setClickedChipID(null); + } else { + console.log("clicked to show"); + setClickedChipID(index); + } + setIsBoxVisible(!isBoxVisible); + }; + + useEffect(() => { + console.log("isBoxVisible: ", isBoxVisible); + console.log("clickedChipID: ", clickedChipID); + }, [isBoxVisible, clickedChipID]); + + return ( +
+

Because you were interested in

+ {data.map( + ( + [ + comicNo, + bookTitle, + mainText, + tooltipText, + tooltipImage, + bookTitle_2, + ], + index + ) => ( +
+ + handleChipClick(comicNo, index)} + /> + +
+ ) + )} + + {isBoxVisible && ( + handleChipClick(data[clickedChipID][0], clickedChipID)} + > + + + + + {"no + + )} +
+ ); +} + +export default function AllChipsWithBox(props) { + const inputData = props.data || cdata; + const outputData = inputData.flatMap( + ({ comic_no, book_title, explanation_lst }) => + explanation_lst.map( + ([mainText, tooltipText, tooltipImage, bookTitle_2]) => [ + comic_no, + book_title, + mainText, + tooltipText, + tooltipImage, + bookTitle_2, + ] + ) + ); + + const data = outputData; + + return ; +} diff --git a/src/components/global_explanation_slider.css b/src/components/global_explanation_slider.css new file mode 100644 index 0000000..79f6d9f --- /dev/null +++ b/src/components/global_explanation_slider.css @@ -0,0 +1,5 @@ +.table-cell-trucate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/src/components/global_explanation_slider.js b/src/components/global_explanation_slider.js new file mode 100644 index 0000000..86edf15 --- /dev/null +++ b/src/components/global_explanation_slider.js @@ -0,0 +1,228 @@ +import React, { useState, useEffect } from "react"; +import { DataGrid } from "@mui/x-data-grid"; +import { Slider, Button, Switch, Tooltip } from "@mui/material"; +import { capitalize } from "@material-ui/core"; +import { Typography } from "@mui/material"; + +const inputKeys = [ + "genre_comb", + "gender", + "panel_ratio", + "supersense", + "comic_cover_img", + "comic_cover_txt", +]; + +const FACET_NAME_OBJ = { + genre_comb: "Genre", + gender: "Gender", + panel_ratio: "Story Pace", + supersense: "Broad Themes from Dialogues", + comic_cover_img: "Visual aspects of Book Cover Image", + comic_cover_txt: "Topics from Book Cover Image", +}; + +const INITIAL_STATE = { + genre_comb: 1.0, + gender: 1.0, + panel_ratio: 1.0, + supersense: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, +}; + +const FACET_NAME_TOOLTIP_LST = { + genre_comb: "Facet captures amount of Action, Humor, Mystery etc in book", + gender: "Gender Proportion such as Male, Female, Other characters in book", + panel_ratio: "Story Pace, Book Length and Ease of Reading the book", + supersense: + "Coarse topics such as weather, adjectives, emotions that are talked about in the dialogues", + comic_cover_img: + "Represents visual aspects of Book cover image like color, objects", + comic_cover_txt: "Textual topics extracted from book cover image", +}; + +const FACET_VALUE_TOOLTIP_LST = { + genre_comb: "More the value, More the emphasis of genre on search results", + gender: "More the value, More the emphasis of gender on search results", + panel_ratio: + "More the value, More the emphasis of story pace on search results", + supersense: + "More the value, More the emphasis of coarse themes on search results", + comic_cover_img: + "More the value, More the emphasis of cover image on search results", + comic_cover_txt: + "More the value, More the emphasis of cover image topics on search results", +}; + +const GlobalExplanationSliderGrid = ({ inputData, onSubmit }) => { + const [data, setData] = useState(inputData); + const [isEditable, setIsEditable] = useState(false); + const [sliderSX, setSliderSX] = useState({ + width: "80%", + "& .MuiSlider-thumb": { color: "gray" }, + "& .MuiSlider-track": { color: "gray" }, + "& .MuiSlider-rail": { color: "#acc4e4" }, + "& .MuiSlider-active": { color: "green" }, + }); + + const handleSliderChange = (key, value) => { + setData((prevData) => ({ ...prevData, [key]: value })); + console.log("submitted user global slider data: ", data); + onSubmit({ + isEditable: isEditable, + userChosenFacetWeights: data, + }); + }; + + // update editable switch and color of sliders + const handleSwitchChange = (event) => { + console.log(event); + setIsEditable((checked) => event.target.checked); + if (event.target.checked) { + setSliderSX({ + width: "80%", + "& .MuiSlider-thumb": { color: "#42a5f5" }, + "& .MuiSlider-track": { color: "#42a5f5" }, + "& .MuiSlider-rail": { color: "#acc4e4" }, + "& .MuiSlider-active": { color: "green" }, + }); + + onSubmit({ + isEditable: event.target.checked, + userChosenFacetWeights: data, + }); + } else { + setSliderSX({ + width: "80%", + "& .MuiSlider-thumb": { color: "gray" }, + "& .MuiSlider-track": { color: "gray" }, + "& .MuiSlider-rail": { color: "#acc4e4" }, + "& .MuiSlider-active": { color: "green" }, + }); + console.log("submitted default data from global slider: ", data); + onSubmit({ + isEditable: event.target.checked, + userChosenFacetWeights: { + genre_comb: 1.0, + gender: 1.0, + panel_ratio: 1.0, + supersense: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + }); + } + }; + + const conditionalGlobalExplanationUpdateFromBackend = () => { + if (isEditable === false) { + setData(inputData); + } + }; + + const sendUsersFacetWeightsToMain = () => { + if (isEditable === true) { + console.log("submitted user global slider data: ", data); + onSubmit(data); + } else { + onSubmit({ + genre_comb: 1.0, + gender: 1.0, + panel_ratio: 1.0, + supersense: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }); + } + }; + + const columns = [ + { + field: "name", + headerName: "Facet Name", + width: 250, + renderCell: (params) => ( + + {FACET_NAME_TOOLTIP_LST[params.row.id]} + + } + > + {params.row.name} + + ), + }, + { + field: "value", + headerName: "Contribution towards Search", + width: 200, + renderCell: (params) => ( + + {FACET_VALUE_TOOLTIP_LST[params.row.id]} + + } + > + + handleSliderChange(params.row.id, value) + } + sx={sliderSX} + /> + + ), + }, + { + field: "isEditable", + headerName: "Edit Contribution", + width: 95, + renderHeader: (params) => ( +
+
{params.headerName}
+ +
+ ), + }, + ]; + + const rows = inputKeys.map((k) => ({ + id: k, + name: FACET_NAME_OBJ[k], + value: data[k], + })); + console.log("rows: ", rows); + + return ( +
+

Facet Contribution Towards Search

+ +
+ ); +}; + +export default GlobalExplanationSliderGrid; diff --git a/src/components/local_explanation.css b/src/components/local_explanation.css index 92ceb20..0fefcb7 100644 --- a/src/components/local_explanation.css +++ b/src/components/local_explanation.css @@ -1,6 +1,11 @@ .local-explanation { font-family: sans-serif; text-align: center; + margin-top: 15px; + margin-bottom: 5px; + height:auto; + width: 570px; + color: whitesmoke; } .facet-container { diff --git a/src/components/local_explanation.js b/src/components/local_explanation.js index 50d2f00..85e8a62 100644 --- a/src/components/local_explanation.js +++ b/src/components/local_explanation.js @@ -2,6 +2,9 @@ import React, { useState } from "react"; import { Bar, Line } from "react-chartjs-2"; import "./local_explanation.css"; +import { Card, CardHeader, CardContent, Grid } from "@mui/material"; +import { capitalize } from "@material-ui/core"; + var labels_lst = ["query_book", "selected_book"]; const DUMMY_LOCAL_EXPLANATION = { @@ -32,21 +35,18 @@ const DUMMY_LOCAL_EXPLANATION = { }; const FACET_KEYS = ["Who", "What", "When", "Why", "Where", "How"]; +const average = (array) => array.reduce((a, b) => a + b) / (array.length + 1); +const round_number = (num) => Math.round((num + Number.EPSILON) * 100) / 100; function LinechartApp(props) { console.log("story pace props: ", props); var query_book_story_pace = props.story_pace[0]; - var selected_book_story_pace = props.story_pace[1]; + var interested_book_story_pace = props.story_pace[1]; var max_len = Math.max( query_book_story_pace.length, - selected_book_story_pace.length + interested_book_story_pace.length ); labels_lst = [...Array(max_len).keys()]; - //console.log("previous pace: ", query_book_story_pace); - //console.log("current pace: ", selected_book_story_pace); - // const [localExplanation, setLocalExplanation] = useState( - // DUMMY_LOCAL_EXPLANATION - // ); const new_state = { data: { labels: labels_lst, @@ -56,38 +56,58 @@ function LinechartApp(props) { borderColor: "rgb(255, 99, 132)", backgroundColor: "rgba(255, 99, 132, 0.5)", data: query_book_story_pace, + lineTension: 0.95, }, { - label: "selected_book", + label: "interested_book", borderColor: "rgb(53, 162, 235)", backgroundColor: "rgba(53, 162, 235, 0.5)", - data: selected_book_story_pace, + data: interested_book_story_pace, + lineTension: 0.95, }, ], }, }; - //setLocalExplanation(new_state); - // useEffect(() => { - // console.log("localExplanation: ", localExplanation); - // }, [localExplanation]); - const options = { responsive: true, legend: { display: true, }, + scales: { + y: { + title: { + display: true, + text: "Story Pace", + font: { + size: 18, // adjust as needed + }, + }, + }, + x: { + title: { + display: true, + text: "Page Number", + font: { + size: 18, // adjust as needed + }, + }, + }, + }, }; return ( - +
+

Story Pace Comparision

+ +
); } @@ -139,4 +159,165 @@ function FacetKeywordsComp(props) { ); } -export { FacetKeywordsComp, LinechartApp }; +const CardGrid = ({ + pagecount, + story_pace, + pagecount_str, + story_pace_str, + color_str, +}) => { + return ( + + + + {pagecount_str}} + titleTypographyProps={{ fontSize: 14 }} + subheaderTypographyProps={{ fontSize: 12 }} + /> + + {{pagecount}} + + + + + + {story_pace_str}} + subheader="higher the number, higher the pace" + titleTypographyProps={{ fontSize: 16 }} + subheaderTypographyProps={{ fontSize: 14 }} + /> + + {{story_pace}} + + + + + ); +}; + +function StoryPaceExplanation(props) { + // data for textual boxes + console.log("story pace props: ", props); + var query_book_story_pace = props.story_pace[0]; + var interested_book_story_pace = props.story_pace[1]; + var query_book_pages = query_book_story_pace.length + 1; + var interested_book_pages = interested_book_story_pace.length + 1; + var panel_ratio_query_book = round_number( + 2 / (average(query_book_story_pace) + 0.01) + ); + var panel_ratio_interested_book = round_number( + 2 / (average(interested_book_story_pace) + 0.01) + ); + + // data for linechart + var max_len = Math.max( + query_book_story_pace.length, + interested_book_story_pace.length + ); + labels_lst = [...Array(max_len).keys()]; + const new_state = { + data: { + labels: labels_lst, + datasets: [ + { + label: "query_book", + borderColor: "rgb(255, 99, 132)", + backgroundColor: "rgba(255, 99, 132, 0.5)", + data: query_book_story_pace, + lineTension: 0.95, + }, + + { + label: "interested_book", + borderColor: "rgb(53, 162, 235)", + backgroundColor: "rgba(53, 162, 235, 0.5)", + data: interested_book_story_pace, + lineTension: 0.95, + }, + ], + }, + }; + + const options = { + responsive: true, + legend: { + display: true, + }, + scales: { + y: { + title: { + display: true, + text: "Panel Count", + font: { + size: 18, // adjust as needed + }, + }, + }, + x: { + title: { + display: true, + text: "Page Number", + font: { + size: 18, // adjust as needed + }, + }, + }, + }, + }; + + if (Math.floor(Math.random() * 10) % 2 === 1) { + return ( +
+

Story Pace Comparision

+ +
+ ); + } else { + return ( +
+

Story Pace Comparision

+ +
+ ); + } +} + +export { FacetKeywordsComp, LinechartApp, StoryPaceExplanation }; + +/* +else { + return ( +
+

Story Pace Comparision

+ + +
+ ); + } +*/ diff --git a/src/components/navbar.css b/src/components/navbar.css new file mode 100644 index 0000000..128ebcb --- /dev/null +++ b/src/components/navbar.css @@ -0,0 +1,89 @@ +.nav-bar { + position: absolute; + width: 74%; + left: 0; + top: 0; + display: flex; + justify-content: center; + align-items: flex-start; + background-color: white; + padding: 10px; + height: 10%; + +} + +.search-container { + display: content; + flex-direction: row; + align-items: center; + justify-content: center; + margin-top: 10px; + width: 70%; + margin-right: auto; + margin-left: auto; +} + +.search-container input { + width: 100%; + border:solid black 2px; + border-style: groove; +} + +.about-us-link { + display: inline-block; + margin-left: 10%; + margin-top: 15px; + font-size: 20px; + font-weight: bold; + color: #333; + text-decoration: none; +} + +.about-us-link:hover { + text-decoration: underline; +} + +.home-logo { + display: inline-block; + margin-left: 4%; + margin-top: 15px; + font-size: 20px; + font-weight: bold; + color: #333; + text-decoration: none; +} + +.dropdown-container { + margin-top: 0.5%; + margin-left: 7%; + margin-right: 2%; + position: relative; + display: inline-block; +} + +.dropdown-content { + display: none; + position: absolute; + z-index: 1; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown:hover .dropdown-content { + display: block; +} + + +/* .nav-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + display: flex; + justify-content: center; +} */ \ No newline at end of file diff --git a/src/components/navbar.js b/src/components/navbar.js new file mode 100644 index 0000000..e00571d --- /dev/null +++ b/src/components/navbar.js @@ -0,0 +1,251 @@ +import React, { useState, useEffect } from "react"; +import "./navbar.css"; +import { useParams, Link, useLocation, useNavigate } from "react-router-dom"; +import SearchContainer from "../components/search_bar"; +import "react-widgets/styles.css"; +import { MenuItem, Select } from "@mui/material"; +import FetchSearchResultsforSearchbarQuery from "../backend_api_calls/FetchSearchResultsforSearchbarQuery"; + +function HomeLogo() { + return ( +
+ + Home + +
+ ); +} + +function NavBar() { + const navigate = useNavigate(); + const [showAbout, setShowAbout] = useState(false); + const [selectedSystem, setSelectedSystem] = useState("Wayne"); // initialize state with default value + + const handleSystemChange = (event) => { + setSelectedSystem(event.target.value); + }; + + useEffect(() => { + console.log("selected system: ", selectedSystem); + sessionStorage.setItem("system_type", JSON.stringify(selectedSystem)); + }, [selectedSystem]); + + function generateGrid(book) { + //event.preventDefault(); + console.log("generateGrid input: ", book); + const searchResults = null; + let queryBook = { + ...book.clickedQuery, + text: book.book_title, + type: "book", + }; + + queryBook = { + id: queryBook.id, + comic_no: queryBook.comic_no, + book_title: queryBook.book_title, + text: queryBook.book_title, + type: queryBook.type, + }; + console.log("generateGrid querybook: ", queryBook); + + // default all facet weights to one + let isEditable = true; + let userFacetWeights = { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }; + try { + let fetchSearchPromise = FetchSearchResultsforSearchbarQuery({ + clickedBook: { ...queryBook }, + isEditable, + userFacetWeights, + }); + + fetchSearchPromise + .then((response) => { + let searchResults = response.data[0]; + console.log("clicked book from landing page: ", { ...queryBook }); + console.log("searchResults in promise: ", searchResults); + console.log("navigate path: ", `/search/${queryBook.comic_no}`); + navigate(`/search/${queryBook.comic_no}`, { + state: { + books: [...searchResults], + query: { + id: queryBook.id, + comic_no: queryBook.comic_no, + book_title: queryBook.book_title, + year: queryBook.year, + genre: queryBook.genre, + query_book: true, + }, + }, + }); + }) + .catch((error) => { + throw error; + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during navigation: ", error); + } + } + + return ( +
+ +
+ + About Us + +
+ +
+ +
+
+ + +
+
+ ); +} + +export default NavBar; + +// import React, { useState } from "react"; +// import "./navbar.css"; +// import { Link, useNavigate } from "react-router-dom"; +// import SearchContainer from "../components/search_bar"; +// import "react-widgets/styles.css"; +// import FetchSearchResultsforSearchbarQuery from "../backend_api_calls/FetchSearchResultsforSearchbarQuery"; + +// function HomeLogo() { +// return ( +//
+// +// Home +// +//
+// ); +// } + +// function NavBar() { +// const navigate = useNavigate(); +// const [showAbout, setShowAbout] = useState(false); + +// function generateGrid(book) { +// console.log("generateGrid input: ", book); +// const searchResults = null; +// let queryBook = { +// ...book.clickedQuery, +// text: book.book_title, +// type: "book", +// }; + +// queryBook = { +// id: queryBook.id, +// comic_no: queryBook.comic_no, +// book_title: queryBook.book_title, +// text: queryBook.book_title, +// type: queryBook.type, +// }; +// console.log("generateGrid querybook: ", queryBook); + +// // default all facet weights to one +// let isEditable = true; +// let userFacetWeights = { +// genre_comb: 1.0, +// supersense: 1.0, +// gender: 1.0, +// panel_ratio: 1.0, +// comic_cover_img: 1.0, +// comic_cover_txt: 1.0, +// }; +// try { +// let fetchSearchPromise = FetchSearchResultsforSearchbarQuery({ +// clickedBook: { ...queryBook }, +// isEditable, +// userFacetWeights, +// }); + +// fetchSearchPromise +// .then((response) => { +// let searchResults = response.data[0]; +// console.log("clicked book from landing page: ", { ...queryBook }); +// console.log("searchResults in promise: ", searchResults); +// navigate(`/search/${book.comic_no}`, { +// state: { +// books: [...searchResults], +// query: { +// id: queryBook.id, +// comic_no: queryBook.comic_no, +// book_title: queryBook.book_title, +// year: queryBook.year, +// genre: queryBook.genre, +// query_book: true, +// }, +// }, +// }); +// }) +// .catch((error) => { +// throw error; +// }); +// } catch (error) { +// // handle rejected Promise/error/etc... +// console.log("error during navigation: ", error); +// } +// } + +// return ( +//
+// +//
+// Home +// About Us +//
+//
+// +//
+//
+// +// Google Form +// +//
+//
+// ); +// } + +// export default NavBar; diff --git a/src/components/search_bar.css b/src/components/search_bar.css index ae05a15..05ad5e8 100644 --- a/src/components/search_bar.css +++ b/src/components/search_bar.css @@ -4,7 +4,7 @@ align-items: center; justify-content: center; margin-top: 20px; - width: 90%; + width: 70%; margin-right: auto; margin-left: auto; } @@ -13,7 +13,7 @@ display: flex; flex-direction: row; align-items: center; - width: 70%; + width: 90%; } .search-select { @@ -39,7 +39,7 @@ width: 150px; height: 40px; font-size: 16px; - background-color: lightblue; + background-color: blueviolet; color: white; border: none; border-radius: 4px; diff --git a/src/components/search_bar.js b/src/components/search_bar.js index 1cc5469..02dc661 100644 --- a/src/components/search_bar.js +++ b/src/components/search_bar.js @@ -106,18 +106,19 @@ const SearchContainer = (props) => { const suggestMatchedBookTitle = (book, typed_query) => { // console.log("type" in book); // console.log(book, typed_query, "type" in book); - if ("type" in book) { - return book; - } else if ( + if ( typeof typed_query === "string" && - book.book_title.toLowerCase().includes(typed_query.toLowerCase()) + (book.book_title.toLowerCase().includes(typed_query.toLowerCase()) || + book.genre.toLowerCase().includes(typed_query.toLowerCase())) ) { return { type: "book", - text: book.book_title, + text: book.book_title + " - " + book.genre, book_title: book.book_title, comic_no: book.comic_no, }; + } else if ("type" in book) { + return book; } else { return { type: "book", text: "", book_title: null, comic_no: null }; } @@ -174,40 +175,39 @@ const SearchContainer = (props) => { var typed_query = e; let suggestedQueryResults = []; - if (typeof e === "string") { - var freeTextQueryDict = { - type: "free text", - text: e, - book_title: null, - comic_no: null, - }; - } else { - var freeTextQueryDict = e; - } + // commented free text query + // if (typeof e === "string") { + // var freeTextQueryDict = { + // type: "free text", + // text: e, + // book_title: null, + // comic_no: null, + // }; + // } else { + // var freeTextQueryDict = e; + // } // console.log("e ", e); - suggestedQueryResults.push(freeTextQueryDict); + // suggestedQueryResults.push(freeTextQueryDict); SEARCHBAR_BOOKS.map((book) => { var matchedBookTitleDict = suggestMatchedBookTitle(book, typed_query); - var matchedBookGenreDict = suggestMatchedBookGenre(book, typed_query); - var matchedBookCharacterList = suggestMatchedBookCharacter( - book, - typed_query - ); + var matchedBookGenreDict = {}; //suggestMatchedBookGenre(book, typed_query); commented as genre mixed with book title + var matchedBookCharacterList = {}; // suggestMatchedBookCharacter(book,typed_query); commented as not implements, character names not present + if (matchedBookTitleDict.comic_no !== null) { suggestedQueryResults.push(matchedBookTitleDict); } - if (matchedBookGenreDict.comic_no !== null) { - suggestedQueryResults.push(matchedBookGenreDict); - } + // if (matchedBookGenreDict.comic_no !== null) { + // suggestedQueryResults.push(matchedBookGenreDict); + // } - matchedBookCharacterList.map((matched_obj) => { - if (matched_obj.comic_no !== null) { - suggestedQueryResults.push(matched_obj); - } - }); + // matchedBookCharacterList.map((matched_obj) => { + // if (matched_obj.comic_no !== null) { + // suggestedQueryResults.push(matched_obj); + // } + // }); return; }); diff --git a/src/index.js b/src/index.js index 882415c..06f1991 100644 --- a/src/index.js +++ b/src/index.js @@ -3,14 +3,9 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; -import { BrowserRouter } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById("root")); -root.render( - - - -); +root.render(); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) diff --git a/src/pages/AboutUs.css b/src/pages/AboutUs.css new file mode 100644 index 0000000..9e2562a --- /dev/null +++ b/src/pages/AboutUs.css @@ -0,0 +1,62 @@ +/* .about-us { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-top: 1%; + width: 74%; + height: auto; + + margin-left: 1%; + margin-bottom: 1%; + background-color: white; + margin-top: 15%; +} */ + +.about-us { +max-width: 100%; +margin: 10% auto; +padding: 0 5%; +text-align: center; +font-size: larger; +} + +.header-section { + text-align: center; + margin-bottom: 40px; +} + +.video-section { + text-align: center; + margin-bottom: 40px; +} + +.contact-section { + margin-bottom: 40px; +} + +.form-section { + text-align: center; +} + +.google-form-button { + display: inline-block; + margin-top: 20px; + padding: 16px 24px; + font-size: 16px; + font-weight: 700; + text-align: center; + text-decoration: none; + color: #fff; + background-color: #4285f4; + border-radius: 8px; + transition: background-color 0.2s ease; +} + +.google-form-button:hover { + background-color: #2c7ff8; +} + +.google-form-button:active { + background-color: #1c5fab; +} diff --git a/src/pages/AboutUs.js b/src/pages/AboutUs.js new file mode 100644 index 0000000..521d3dc --- /dev/null +++ b/src/pages/AboutUs.js @@ -0,0 +1,64 @@ +import React from "react"; +import "./AboutUs.css"; + +function AboutUs() { + return ( +
+
+

About Us

+

+ Welcome to our comic book search system! We are passionate about + helping comic book enthusiasts find the books they love. We understand + the challenges of finding the perfect book. We've created a search + engine that takes into account not only genre and theme, but also more + nuanced factors such as story pace and comic book cover art. We + believe that every comic book reader has unique tastes, and our system + is designed to help personalize the search experience for each user. + User acts as co-pilot of the system and can take over the + personalization from the system to tune it as they like. We also + attempt to provide you with answers on "Why did i get these search + results?" through local and global explanations. We welcome feedback + from our users and are always open to suggestions for how we can + better serve the comic book community. Thank you for using our search + system and we hope you find your next favorite comic book! +

+
+
+ +
+
+

Contact Us

+
    +
  • Email: suraj.shashidhar@st.ovgu.de
  • +
  • Phone: 123-456-7890
  • +
  • Address: Ovgu, Magdeburg
  • +
+
+
+

Submit Your Feedback

+

+ Have a suggestion or found a bug? Let us know by filling out our + feedback form. +

+ + Submit Feedback + +
+
+ ); +} + +export default AboutUs; diff --git a/src/pages/BookGrid.css b/src/pages/BookGrid.css new file mode 100644 index 0000000..ec3b589 --- /dev/null +++ b/src/pages/BookGrid.css @@ -0,0 +1,423 @@ +.app-container { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + +} + +.nav-bar { + position: absolute; + width: 74%; + left: 0; + top: 0; + display: flex; + justify-content: center; + align-items: flex-start; + background-color: white; + padding: 10px; + height: 10%; + +} + + +.search-container { + display: content; + flex-direction: row; + align-items: center; + justify-content: center; + margin-top: 5px; + width: auto; + margin-right: auto; + margin-left: auto; +} + +.search-container input { + width: 100%; + border:solid black 2px; + border-style: groove; +} + +.book-container { + display: flex; + flex-direction: column; + align-items: end; + justify-content: end; + width: 500px; + height: 400px; + margin: 10px; + border: 10px solid black; + border-radius: 20px; +} + +.book-name { + font-size: 20px; + font-weight: bold; + margin-bottom: 10px; +} + +.book-genre { + font-size: 16px; + margin-bottom: 10px; +} + + +.query-seed-button { + width: 90%; + height: 20px; + background-color: lightblue; + border: 1px solid black; + border-radius: 10px; + font-size: 12px; + font-weight: bold; + cursor: pointer; +} + +.grid-container { + display: grid; + grid-template-columns: repeat(6, 1fr); + grid-template-rows: repeat(3, 1fr); + grid-gap: 2rem; + width: 75%; + +} + +.grid-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2px; + background-color: #fff; + border-radius: 10px; + cursor: pointer; +} + +.global-explanation-container { + padding: 10px; + text-align: center; + height: 28em; + width: auto; + display: flex; + position: relative; + margin-top: 2em; + color: white; +} + +.global-explanation-slider { + padding: 1em; + text-align: center; + height: 70em; + width: auto; + display: flex; + position:absolute; + margin-top: 3em; + margin-left: 1em; + margin-right: 2em; + margin-bottom: 3em +} +.global-explanation-chips { + padding: 1em; + text-align: center; + height: 2em; + display: flex; + position: relative; + margin-top: 2em; + +} + +.local-explanation-container { + padding: 1em; + text-align: center; + height: auto; + width: auto; + display: flex; + position:relative; + margin-top: 5em; + margin-bottom: 2em; +} + +.side-bar-container { + background-color: black; + padding: 5px; + width: 25%; + height: 100%; + position: absolute; + right: 0; + top: 0; + bottom: 0; + border-left: 3mm ridge; + border-left-style: groove; + border-left-color: black; +} + +.main-screen-old{ + padding-left: 2%; + padding-top: 1%; + margin-top: 7%; + width: 74%; + display: flex; + flex-direction: column; + justify-content:flex-start; + align-items: center; + height: 90%; + padding-top: 10px; /* Adjust as needed */ +} +.main-screen { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + padding-top: 1%; /* Adjust as needed */ + width: 74%; + height: auto; + + margin-left: 1em; + margin-bottom: 1em; + background-color: white; + margin-top: 3em; +} + +.book-object { + width: 90%; + height: 100%; + margin: 10px; + border: 1px #ccc; + border-radius: 5px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.book-object img { + width: 100%; + height: 100%; + align-items: flex-start; + object-fit: cover; + border-radius: 1px 1px 0 0; +} + +.book-object p { + margin: 1px 0; +} + + +/* Added new grid styles */ +.book-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + grid-auto-rows: repeat(3, 1fr); + gap: 1rem; + margin-top: 2.5em; + margin-left: 1em; + margin-right: 1em; + margin-bottom: 1em; + width: 100%; + height: 100%; + position: relative; + /* background: rgb(2, 0, 36); + background: linear-gradient( + 90deg, + rgba(10, 10, 36, 1) 0%, + rgba(200, 30, 30, 1) 89% + ); */ +} + +@media screen and (max-width: 768px) { + .book-grid { + grid-template-columns: repeat(5, 1fr); + } +} + +.book { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.book img { + display: cover; + width: 60%; + height: auto; +} + +.book-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: gainsboro; + opacity: 0; + transition: opacity 1s ease-in-out; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.book:hover .book-overlay { + opacity: 0.9; + transition-delay: 1s; +} + +.book-overlay h2 { + font-size: 1rem; + margin-bottom: 0.5rem; + color: black; +} + +.book-overlay p { + font-size: 1.0rem; + color: black; +} + +.link { + width: 100%; + height: 100%; +} + +.book-card { + object-fit: cover; + width: auto; + height: auto; + position: relative; +} + +.imageLarge { + margin: 1em 5px 17px 0; + display: block; + width: 70%; + height: auto; + transition: transform 450ms; + border-radius: 5px; + cursor: pointer; +} + +.imageLarge:hover { + transform: scale(1.08); +} + +.thumbs { + display: flex; + align-items: center; + justify-content: space-evenly; + margin-top: 1em; +} + +.thumbs-up, +.thumbs-down { + cursor: pointer; + font-size: 16px; +} + +.thumbs-up { + color: green; +} + +.thumbs-down { + color: red; +} + +.view-book-button { + background-color: #731e88; /* Purple */ + border: none; + color: white; + padding: 8px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 2px 2px; + cursor: pointer; + border-radius: 16px; +} + +.search-progress-container { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.5); +} + +​ + + +/* Modal css*/ +/* Modal */ +/* .modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; + background-color: #fff; + border-radius: 10px; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.5); + padding: 20px; + max-width: 90%; + max-height: 90%; + overflow-y: auto; +} + +/* Backdrop */ +/* .backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 9998; + background-color: rgba(0, 0, 0, 0.5); +} */ + +/* Fade effect */ +/* .fade-enter { + opacity: 0; +} */ + +/* .fade-enter-active { + opacity: 1; + transition: opacity 300ms ease-in-out; +} */ + +/* .fade-exit { + opacity: 1; +} */ + +/* .fade-exit-active { + opacity: 0; + transition: opacity 300ms ease-in-out; +} */ + +.compare-books-container{ + margin-top: 2.5em; + margin-left: 1em; + margin-right: 1em; + margin-bottom: 1em; + width: 85%; + height: auto; + position: fixed; +} +.compare-books-box { + position: fixed; + top: 50%; + left: 40%; + transform: translate(-50%, -50%); + width: 700; + background-color: whitesmoke; + border: 2px solid #000; + box-shadow: 24; + align-items: center; + display: flex; + justify-content: center; +} diff --git a/src/pages/BookGrid.js b/src/pages/BookGrid.js new file mode 100644 index 0000000..852c428 --- /dev/null +++ b/src/pages/BookGrid.js @@ -0,0 +1,908 @@ +import axios from "axios"; +import "./BookGrid.css"; +import BarchartApp from "../components/global_explanation"; +import { + LinechartApp, + FacetKeywordsComp, + StoryPaceExplanation, +} from "../components/local_explanation"; +import SearchContainer from "../components/search_bar"; +import CompareBooks from "../components/CompareBooks"; +import { + SEARCHBAR_BOOKS, + DUMMY_BOOKS, + DEFAULT_LOCAL_EXPLANATION, + FACET_KEYS, +} from "../components/constants"; +import React, { useState, useRef, useEffect } from "react"; +import { useParams, Link, useLocation } from "react-router-dom"; +import GlobalExplanationSliderGrid from "../components/global_explanation_slider"; +import FetchSearchResultsforSearchbarQuery from "../backend_api_calls/FetchSearchResultsforSearchbarQuery"; +import FetchSearchResultsForBookGrid from "../backend_api_calls/FetchSearchResultsForBookGrid"; +import FetchComicPDF from "../backend_api_calls/FetchComicPDF"; +import FetchCompareExplanations from "../backend_api_calls/FetchCompareExplanations"; +import FetchLocalExplanations from "../backend_api_calls/FetchLocalExplanations"; +import ExplanationChips from "../components/global_explanation_relevance_feedback"; +import AllChipsWithBox from "../components/global_explanation_relevance_feedback_new"; +import { makeStyles } from "@material-ui/core/styles"; +import Chip from "@material-ui/core/Chip"; +import Tooltip from "@material-ui/core/Tooltip"; +import CircularProgress from "@mui/material/CircularProgress"; +import Checkbox from "@mui/material/Checkbox"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import { Modal, Backdrop, Fade, Box } from "@mui/material"; + +const img_folderpath = "../../comic_book_covers_ui/"; ///process.env.PUBLIC_URL + Users/surajshashidhar/Downloads/comic_book_covers_ui"; + +const initial_chips = [ + { + comic_no: 0, + book_title: "could not determine your interests for this search", + genre: "No Genre", + year: 1950, + query_book: false, + explanation_lst: [["No Themes Found"]], + }, +]; + +// function HomeLogo() { +// return ( +//
+// +// Home +// +//
+// ); +// } + +function BookGrid(props) { + const { state } = useLocation(); + let books_from_landing_page = null; + let q = null; + let { id } = useParams(); + + useEffect(() => { + console.log("state: ", state); + if (id == null) { + console.log("clicked path bookgrid : ", "/search/1"); + } else { + console.log("clicked path bookgrid : ", `/search/${id}`); + } + // console.log("clicked path: ", `/search/${id}`); + }, [id]); + + const [searchValue, setSearchValue] = useState(""); + const [books, setBooks] = useState(DUMMY_BOOKS); + const [currentQueryBook, setCurrentQueryBook] = useState(null); + const [filteredBooks, setFilteredBooks] = useState([]); + const [searchText, setSearchText] = useState(""); + const [globalExplanation, setGlobalExplanation] = useState([ + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + ]); + const [searchBarInput, setSearchBarInput] = useState({ + typedQuery: "", + clickedQuery: {}, + }); + const [localExplanation, setLocalExplanation] = useState( + DEFAULT_LOCAL_EXPLANATION + ); + + // new states using hover + // handle hovering or interested books + const [hoveredBook, setHoveredBook] = useState(null); + // for delaying hovering action, delay of 1 second before capturing hover + const [showOverlay, setShowOverlay] = useState(false); + const timeoutRef = useRef(null); + const [hoveredBookList, setHoveredBookList] = useState([]); + const [isEditable, setIsEditable] = useState(false); + const [relevanceFeedbackExplanation, setRelevanceFeedbackExplanation] = + useState(initial_chips); + + // loading symbol + const [bookLoading, setBookLoading] = useState(false); + const [searchLoading, setSearchLoading] = useState(false); + const [compareBooksCheckedList, setCompareBooksCheckedList] = React.useState( + [] + ); + const [compareBookLoading, setCompareBookLoading] = useState(false); + const [openBackdrop, setOpenBackdrop] = useState(false); + const [comparedBookExplanations, setComparedBookExplanations] = useState({}); + + const handleMouseEnter = (book) => { + timeoutRef.current = setTimeout(() => { + setShowOverlay(true); + setHoveredBook(book); + setHoveredBookList((prevList) => [...prevList, book]); + console.log("hovered book: ", book); + console.log("hovered book list: ", hoveredBookList.length); + // get session storage + sessionStorage.setItem( + "hoveredBookList", + JSON.stringify(hoveredBookList) + ); + // show local explanations + var localExplanationInfoJSON = { + selected_book_lst: [{ ...currentQueryBook, query_book: true }, book], + }; + console.log("localExplanationInfoJSON: ", localExplanationInfoJSON); + + let fetchLocalExplanationsPromise = FetchLocalExplanations( + localExplanationInfoJSON + ); + + fetchLocalExplanationsPromise + .then((response) => { + setLocalExplanation(response); + console.log("retrieved local explanation"); + }) + .catch((error) => { + console.log("error while etrieving local explantions: ", error); + }); + }, 1500); + }; + + const handleMouseLeave = () => { + clearTimeout(timeoutRef.current); + setShowOverlay(false); + setHoveredBook(null); + setLocalExplanation(DEFAULT_LOCAL_EXPLANATION); + }; + + const handleClick = (book) => { + // issue for query only if it is not already query book if (!book.query_book) + if (book.comic_no !== null) { + // add the current path to the browser history state + // const currentPath = window.location.pathname; + // window.history.pushState({ previousPath: currentPath }, ""); + + // show spinner while loading + setSearchLoading((prevState) => true); + + // set selected query book as clicked book + setCurrentQueryBook(book); + + setCompareBooksCheckedList((prevList) => [book]); + + // find uninterested books which were not hovered by user + var unInterestedBookList = books + .filter( + ({ comic_no: id1 }) => + !hoveredBookList.some(({ comic_no: id2 }) => id2 === id1) + ) + .map((e) => { + e.interested = 0.0; + return e; + }); + + // remove duplicate clicks + var allBookInteractionInfoList = [ + ...hoveredBookList.map((e) => { + e.interested = 1.0; + return e; + }), + ]; + var allBookInteractionInfoUniqList = [ + ...new Map( + allBookInteractionInfoList.map((v) => [v.comic_no, v]) + ).values(), + ]; + + // add uninterested and interested books + var allBookInteractionInfoJSON = { + interested_book_lst: [ + ...allBookInteractionInfoUniqList, + ...unInterestedBookList, + ], + }; + console.log("all interaction info books ", allBookInteractionInfoJSON); + try { + let fetchSearchResultsforBookGridPromise = + FetchSearchResultsForBookGrid( + book.comic_no, + isEditable, + allBookInteractionInfoJSON, + globalExplanation[1] + ); + fetchSearchResultsforBookGridPromise + .then((response) => { + setHoveredBookList([]); // clear the current window and set clicked list as query book + setBooks(response.data[0]); + setFilteredBooks(response.data[0]); + setGlobalExplanation([globalExplanation[1], response.data[1]]); + setRelevanceFeedbackExplanation( + response.data[2].relevance_feedback_explanation + ); + console.log("New Global Explanation: ", globalExplanation); + console.log( + "relevance feedback explanation: ", + response.data[2].relevance_feedback_explanation + ); + + // hide spinner after loading + setSearchLoading((prevState) => false); + + // adding results to session storage + sessionStorage.setItem("books", JSON.stringify(response.data[0])); + sessionStorage.setItem( + "filteredBooks", + JSON.stringify(response.data[0]) + ); + sessionStorage.setItem( + "globalExplanation", + JSON.stringify([globalExplanation[1], response.data[1]]) + ); + sessionStorage.setItem("hoveredBookList", JSON.stringify([])); + sessionStorage.setItem("currentQueryBook", JSON.stringify(book)); + sessionStorage.setItem( + "relevanceFeedbackExplanation", + JSON.stringify(response.data[2].relevance_feedback_explanation) + ); + sessionStorage.setItem( + "compareBooksCheckedList", + JSON.stringify([book]) + ); + }) + .catch((error) => { + throw error; + // hide spinner after loading + setSearchLoading(false); + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during navigation: ", error); + } + } + }; + + const handleSearchBarQueryClick = (queryBook) => { + if (queryBook) { + var searchBarQuery = { ...queryBook }; + console.log("searchBarQuery: ", searchBarQuery); + try { + let fetchSearchResultsforSearchBarPromise = + FetchSearchResultsforSearchbarQuery( + { clickedBook: { ...queryBook } }, + isEditable, + globalExplanation[1] + ); + + fetchSearchResultsforSearchBarPromise + .then((response) => { + var defaultHoveredBookList = [ + { + id: searchBarQuery.id, + comic_no: searchBarQuery.comic_no, + book_title: searchBarQuery.book_title, + genre: "", + year: 1950, + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + ]; + if (searchBarQuery.type === "book") { + setHoveredBookList(defaultHoveredBookList); // clear the current window and set clicked list as query book + } + + // console.log(response); + // console.log("current clicked book list ", currentWindowClickedBookList); + setBooks(response.data[0]); + setFilteredBooks(response.data[0]); + setGlobalExplanation([ + globalExplanation[1], + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + ]); + console.log("New Global Explanation: ", globalExplanation); + console.log( + "search results for searchbar input: ", + response.data[0] + ); + + // adding results to session storage + sessionStorage.setItem("books", JSON.stringify(response.data[0])); + sessionStorage.setItem( + "filteredBooks", + JSON.stringify(response.data[0]) + ); + sessionStorage.setItem( + "globalExplanation", + JSON.stringify([globalExplanation[1], response.data[1]]) + ); + sessionStorage.setItem( + "hoveredBookList", + JSON.stringify(defaultHoveredBookList) + ); + sessionStorage.setItem( + "currentQueryBook", + JSON.stringify({ + id: searchBarQuery.id, + comic_no: searchBarQuery.comic_no, + book_title: searchBarQuery.book_title, + genre: "", + year: 1950, + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }) + ); + sessionStorage.setItem( + "relevanceFeedbackExplanation", + JSON.stringify(response.data[2].relevance_feedback_explanation) + ); + }) + .catch((error) => { + throw error; + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during navigation: ", error); + } + } + + if (searchBarQuery.type === "book") { + // set selected query book as clicked book + var tmp_query_book = { + id: searchBarQuery.id, + comic_no: searchBarQuery.comic_no, + book_title: searchBarQuery.book_title, + genre: "", + year: 1950, + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }; + + setCurrentQueryBook(tmp_query_book); + setCompareBooksCheckedList((prevList) => [tmp_query_book]); + } + }; + + const ProcessSearchBarInput = (searchBarSelectedData) => { + console.log("received form search bar: ", searchBarInput); + setSearchBarInput(searchBarSelectedData); + + console.log("searchBarInput: ", searchBarInput); + }; + + // handle users changes on slider + const handleGlobalExplanationSliderSubmit = (data) => { + if (data.isEditable === true) { + var newGlobalExplanation = [ + globalExplanation[1], + data.userChosenFacetWeights, + ]; + + // set state of global explnanation to user provided and set editable flag + setGlobalExplanation(newGlobalExplanation); + setIsEditable((previsEditable) => true); + sessionStorage.setItem( + "globalExplanation", + JSON.stringify([globalExplanation[1], data.userChosenFacetWeights]) + ); + console.log("User updated global explanation: ", data); + } else { + setIsEditable((previsEditable) => false); + } + }; + + // handle thunbs up and thumbs down + const handleThumbsUp = (book) => { + const updatedBooks = [...books].map((b) => + b.comic_no === book.comic_no ? { ...b, thumbsUp: 1 } : b + ); + setBooks([...updatedBooks]); + }; + + const handleThumbsDown = (book) => { + const updatedBooks = [...books].map((b) => + b.comic_no === book.comic_no ? { ...b, thumbsDown: 1 } : b + ); + setBooks([...updatedBooks]); + }; + + const handleViewBook = async (book) => { + console.log("view book ", book); + setBookLoading((prevState) => true); + + try { + let viewPDFBookPromise = FetchComicPDF(book); + + viewPDFBookPromise + .then((response) => { + // Create a new Blob object from the response data + const blob = new Blob([response.data], { type: "application/pdf" }); + // Create a URL for the Blob object + const url = URL.createObjectURL(blob); + console.log("url pdf : ", url); + + // Open a new tab with the PDF file + window.open(url); + }) + .catch((error) => { + throw error; + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during viewing pdf: ", error); + } finally { + setBookLoading((prevState) => false); + } + + // // Create a new Blob object from the response data + // const blob = new Blob([response.data], { type: "application/pdf" }); + // // Create a URL for the Blob object + // const url = URL.createObjectURL(blob); + // // Open a new tab with the PDF file + // window.open(url); + // } catch (error) { + // console.error(error); + // } finally { + // setBookLoading((prevState) => false); + // } + }; + + const addToCompareBooks = (book, checked) => { + //setChecked(event.target.checked); + if (!checked) { + setCompareBooksCheckedList((prevList) => [ + ...prevList.filter((obj) => obj.comic_no !== book.comic_no), + ]); + } else { + setCompareBooksCheckedList((prevList) => [...prevList, book]); + } + }; + + const handleCompareBooks = async (compareBooksCheckedList) => { + try { + console.log( + "sending comparision query to backend ", + compareBooksCheckedList + ); + setCompareBookLoading((prevState) => true); + + let compareBooksPromise = FetchCompareExplanations( + compareBooksCheckedList + ); + + compareBooksPromise + .then((response) => { + // console.log("comparision books response from backend: ", response.data); + setComparedBookExplanations(() => response.data); + }) + .catch((error) => { + console.error(error); + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during comparision: ", error); + } + }; + + const handleOpenBackdrop = () => { + setOpenBackdrop(true); + handleCompareBooks(compareBooksCheckedList); + }; + + const handleCloseBackdrop = () => { + setOpenBackdrop(false); + }; + + // added local storage to persist state on refresh + useEffect(() => { + if (state && state.books && state.query) { + books_from_landing_page = state && state.books; + q = state && state.query; + console.log( + "book recieved for landing page query: ", + books_from_landing_page + ); + setBooks(() => [...books_from_landing_page]); + setCurrentQueryBook(() => q); + setCompareBooksCheckedList((prevList) => [q]); + + // adding results to session storage + sessionStorage.setItem("books", JSON.stringify(books_from_landing_page)); + sessionStorage.setItem( + "filteredBooks", + JSON.stringify(books_from_landing_page) + ); + sessionStorage.setItem( + "globalExplanation", + JSON.stringify([ + globalExplanation[1], + { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }, + ]) + ); + sessionStorage.setItem("hoveredBookList", JSON.stringify([])); + sessionStorage.setItem("currentQueryBook", JSON.stringify(q)); + sessionStorage.setItem( + "relevanceFeedbackExplanation", + JSON.stringify(initial_chips) + ); + } else { + console.log("page refreshed"); + + // get values from session storage + //setBooks(() => [...books]); + setBooks(() => [...JSON.parse(sessionStorage.getItem("books"))]); + setHoveredBookList(() => [ + ...JSON.parse(sessionStorage.getItem("hoveredBookList")), + ]); + setFilteredBooks(() => [ + ...JSON.parse(sessionStorage.getItem("filteredBooks")), + ]); + setCurrentQueryBook(() => + JSON.parse(sessionStorage.getItem("currentQueryBook")) + ); + setGlobalExplanation(() => + JSON.parse(sessionStorage.getItem("globalExplanation")) + ); + setSearchBarInput(() => + JSON.parse(sessionStorage.getItem("searchBarInput")) + ); + setRelevanceFeedbackExplanation(() => + JSON.parse(sessionStorage.getItem("relevanceFeedbackExplanation")) + ); + setCompareBooksCheckedList(() => + JSON.parse(sessionStorage.getItem("compareBooksCheckedList")) + ); + console.log("states refreshed"); + } + // state = null; + }, [state]); + + useEffect(() => { + if ( + searchBarInput && + searchBarInput.typedQuery && + searchBarInput.clickedQuery + ) { + console.log("useeffect searchBarSelectedData: ", searchBarInput); + var queryBook = searchBarInput.clickedQuery; + handleSearchBarQueryClick(queryBook); + console.log("state of books: ", books); + } + }, [searchBarInput]); + + useEffect(() => { + console.log("state has changed unique, initalized states"); + }, []); + + useEffect(() => { + console.log(relevanceFeedbackExplanation); + }, [relevanceFeedbackExplanation]); + + //detect refresh event and alert user + useEffect(() => { + window.onbeforeunload = function () { + // hide spinner after loading + setSearchLoading((prevState) => true); + console.log( + "detected page refresh, initializing states from session storage" + ); + + setTimeout(() => { + /* Code to run after 4 seconds */ + // hide spinner after loading + setSearchLoading((prevState) => false); + }, 4000); + return true; + }; + + return () => { + console.log("window null setting"); + window.onbeforeunload = null; + }; + }, []); + + useEffect(() => { + console.log("compare books: ", compareBooksCheckedList); + }, [compareBooksCheckedList]); + + useEffect(() => { + console.log("open backdrop: ", openBackdrop); + }, [openBackdrop]); + + useEffect(() => { + console.log( + "compare books response from backend: ", + comparedBookExplanations + ); + }, [comparedBookExplanations]); + + return ( +
+ {/*
+ +
+ +
+
*/} +
+
+
+ +
+
+ {searchLoading ? ( +
+ +
+ ) : ( +
+ {books && + books.slice(0, 15).map((book) => ( +
+ +
handleMouseEnter(book)} + onMouseLeave={() => handleMouseLeave()} + onClick={() => handleClick(book)} + style={ + currentQueryBook === book.id + ? { border: "3px solid blue" } + : {} + } + > + {book.book_title} + {hoveredBook === book && showOverlay && ( +
+

+ + + +

+

+ {Array.from(book.genre.split("|")).map( + (genre_str, index) => ( + + + + ) + )} +

+

+ {FACET_KEYS.map((key) => + Array.from(localExplanation[3][key]).map( + (facet_str, index) => ( + + + + ) + ) + )} +

+
+ )} +
+ + {/* for thumbs up and down so that it doesnt route it to link */} +
+ {/* */} +
+ +
+ {/* + addToCompareBooks(book, event.target.checked) + } + inputProps={{ "aria-label": "controlled" }} + key={book.comic_no + "-" + "checkbox_compare"} + /> */} + +
+ + addToCompareBooks(book, event.target.checked) + } + /> + } + label={`${book.book_title}`} + /> +
+ {/* */} +
+
+ ))} +
+ )} + +
+ + + + + +
+
+ +
+
+ {currentQueryBook && ( +

+ Your Selection: {currentQueryBook.book_title} -{" "} + {currentQueryBook.comic_no} +

+ )} + {hoveredBook && ( +

+ Your Interest: {hoveredBook.book_title} - {hoveredBook.comic_no} +

+ )} +
+ +
+ +
+ + {/*
+ +
*/} + +
+ {/* */} +
+ +
+ + {/*
+
+ +
+
*/} +
+ +
+ + {/*
+ +
*/} +
+
+
+ ); +} + +export default BookGrid; diff --git a/src/pages/LandingPage.css b/src/pages/LandingPage.css new file mode 100644 index 0000000..f97eec1 --- /dev/null +++ b/src/pages/LandingPage.css @@ -0,0 +1,15 @@ +.landing-page{ + display: flex; + position:relative; + flex-direction: column; + justify-content: flex-start; + align-items: left; + padding-top: 1%; /* Adjust as needed */ + width: auto; + height: auto; + + margin-left: 1%; + margin-bottom: 1%; + background-color: white; + margin-top: 5%; +} \ No newline at end of file diff --git a/src/pages/LandingPage.js b/src/pages/LandingPage.js new file mode 100644 index 0000000..fd42e09 --- /dev/null +++ b/src/pages/LandingPage.js @@ -0,0 +1,22 @@ +import RowContainer from "./RowContainer"; +import { + FAMOUS_TITLES_LIST, + CLASSICS_LIST, + NEW_ISSUES_LIST, +} from "../components/constants"; +import "./LandingPage.css"; + +function LandingPage() { + console.log("inside landing page"); + return ( +
+ +
+ ); +} + +export default LandingPage; diff --git a/src/pages/Row.css b/src/pages/Row.css new file mode 100644 index 0000000..84d9161 --- /dev/null +++ b/src/pages/Row.css @@ -0,0 +1,77 @@ +.row { + position: relative; + margin-left: 51px; + color: white; +} + +.column { + margin: 20px 51px 0 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); + grid-gap: 8px; + gap: 2rem; + + @media (max-width: 1000px) { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + } +} + +.column-link { + height: 100%; + width: auto; +} + +.row-container { + display: flex; + overflow-y: hidden; + overflow-x: scroll; + padding-right: 20px; +} + +.row-container::-webkit-scrollbar { + display: none; +} + +.title { + font-size: 1.8vw; + color: black; + font-weight: 700; + margin: 1.6em 0 -0.5em 0; + min-width: 6em; + margin-bottom: 1rem; +} + +.title:hover { + color: #fff; +} + +.image { + object-fit: contain; + max-height: auto; + margin: 17px 5px 17px 0; + transition: transform 450ms; + border-radius: 3px; + cursor: pointer; +} + +.image:hover { + transform: scale(1.2); +} + +.imageLarge { + margin: 1em 5px 17px 0; + width: 90%; + max-height: 450px; + transition: transform 450ms; + border-radius: 5px; + cursor: pointer; +} + +.imageLarge:hover { + transform: scale(1.08); +} + +.link { + width: 100%; + height: 100%; +} diff --git a/src/pages/Row.js b/src/pages/Row.js new file mode 100644 index 0000000..c366651 --- /dev/null +++ b/src/pages/Row.js @@ -0,0 +1,123 @@ +import React from "react"; +import { Link, useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; +import "./Row.css"; +import FetchSearchResultsforSearchbarQuery from "../backend_api_calls/FetchSearchResultsforSearchbarQuery"; +import { delay } from "../components/constants"; + +function Row({ category, bookList, isColumn }) { + // add api data + + const provided_category = category; + console.log(bookList); + var data = bookList; + console.log("data: ", data, " category: ", provided_category, typeof data); + const navigate = useNavigate(); + + function generateGrid(book) { + console.log("generateGrid: ", book); + //event.preventDefault(); + const searchResults = null; + let queryBook = { ...book, text: book.book_title, type: "book" }; + queryBook = { + id: queryBook.id, + comic_no: queryBook.comic_no, + book_title: queryBook.book_title, + text: queryBook.text, + type: queryBook.type, + year: queryBook.year, + }; + + // default all facet weights to one + let isEditable = true; + let userFacetWeights = { + genre_comb: 1.0, + supersense: 1.0, + gender: 1.0, + panel_ratio: 1.0, + comic_cover_img: 1.0, + comic_cover_txt: 1.0, + }; + try { + let fetchSearchPromise = FetchSearchResultsforSearchbarQuery({ + clickedBook: { ...queryBook }, + isEditable, + userFacetWeights, + }); + + fetchSearchPromise + .then((response) => { + let searchResults = response.data[0]; + console.log("clicked book from landing page: ", { ...queryBook }); + console.log("searchResults in promise: ", searchResults); + navigate(`/search/${book.comic_no}`, { + state: { + books: [...searchResults], + query: { + id: queryBook.id, + comic_no: queryBook.comic_no, + book_title: queryBook.book_title, + year: queryBook.year, + genre: queryBook.genre, + query_book: true, + }, + }, + }); + }) + .catch((error) => { + throw error; + }); + } catch (error) { + // handle rejected Promise/error/etc... + console.log("error during navigation: ", error); + } + } + + return ( +
+

{category}

+ +
+ {data + ? data.map((book_obj) => { + const book = book_obj; + return ( +
+ {book.book_title} generateGrid(book)} + /> + {/* */} +
+ ); + }) + : null} +
+
+ ); +} + +Row.defaultProps = { + category: "Trending", + bookList: [ + { + id: 12, + comic_no: 12, + book_title: "Brenda Starr - Silver lining in sun valley", + backdrop_path: "../../comic_book_covers_ui/original_12_1.jpeg", + genre: "detective|female", + }, + ], + isColumn: false, +}; + +Row.propTypes = { + category: PropTypes.string, + bookList: PropTypes.array, + isColumn: PropTypes.bool, +}; + +export default Row; diff --git a/src/pages/RowContainer.css b/src/pages/RowContainer.css new file mode 100644 index 0000000..eb40989 --- /dev/null +++ b/src/pages/RowContainer.css @@ -0,0 +1,29 @@ +.container { + margin-top: 10%; + z-index: 10; +} + +.image { + object-fit: contain; + max-width: 70%; + max-height: 150px; + transition: transform 450ms; + border-radius: 3px; + cursor: pointer; + margin-bottom: 35px; +} + +.image:hover { + transform: scale(1.2); +} + +.container-result { + margin: 120px 51px 0 51px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); + grid-gap: 7px; + + @media (max-width: 1000px) { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + } +} diff --git a/src/pages/RowContainer.js b/src/pages/RowContainer.js new file mode 100644 index 0000000..dbe0118 --- /dev/null +++ b/src/pages/RowContainer.js @@ -0,0 +1,27 @@ +import React from "react"; +import Row from "./Row"; +import styles from "./RowContainer.css"; + +const RowContainer = ({ + famous_titles_booklist, + classics_booklist, + new_issues_booklist, +}) => ( +
+ + Famous Titles + + + Classics + + + New Issues + +
+); + +export default RowContainer; diff --git a/src/pages/StartSession.css b/src/pages/StartSession.css new file mode 100644 index 0000000..0247ecf --- /dev/null +++ b/src/pages/StartSession.css @@ -0,0 +1,21 @@ +.start-session-container { + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.start-session-button { + padding: 20px; + font-size: 24px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.start-session-button:hover { + background-color: #0062cc; +} diff --git a/src/pages/StartSession.js b/src/pages/StartSession.js new file mode 100644 index 0000000..1b67bda --- /dev/null +++ b/src/pages/StartSession.js @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import axios from "axios"; +import "./StartSession.css"; + +function StartSession() { + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + + const handleStartSession = async () => { + try { + setLoading(true); + + const response = await axios.get("http://localhost:8000/start_session", { + params: { + flag: "startnewsession", + }, + }); + + const sessionID = response.data.session_id; + sessionStorage.setItem("sessionID", sessionID); + console.log("session id: ", sessionID); + navigate("/"); + } catch (error) { + console.error(error); + // Handle error here + } finally { + setLoading(false); + } + }; + + return ( +
+ +
+ ); +} + +export default StartSession; diff --git a/src/routes/AppRoutes.js b/src/routes/AppRoutes.js new file mode 100644 index 0000000..5f163fa --- /dev/null +++ b/src/routes/AppRoutes.js @@ -0,0 +1,28 @@ +import React from "react"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +//import { LastLocationProvider } from "react-router-last-location"; +import LandingPage from "../pages/LandingPage"; +import BookGrid from "../pages/BookGrid"; +import SearchContainer from "../components/search_bar"; +import NavBar from "../components/navbar"; +import AboutUs from "../pages/AboutUs"; +import StartSession from "../pages/StartSession"; + +const AppRoutes = () => { + return ( +
+ + + + } exact /> + } exact /> + } /> + } exact /> + } /> + + +
+ ); +}; + +export default AppRoutes;