diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..637c9d93 Binary files /dev/null and b/.DS_Store differ diff --git a/.env.example b/.env.example deleted file mode 100644 index f542c9b4..00000000 --- a/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -API_URL=http://localhost:8080 -REACT_APP_AUTH0_DOMAIN=dev-auth0domain-goes-here.us.auth0.com -REACT_APP_AUTH0_CLIENT_ID=some-client-id-goes-here -REACT_APP_AUTH0_AUDIENCE=https://dev-find-this-under-APIs-in-the-dashboard.us.auth0.com/api/v2/ diff --git a/.gitignore b/.gitignore index 4f5ff540..84e1498d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules .env -dist/main.* \ No newline at end of file +dist/main.* + +.DS_Store \ No newline at end of file diff --git a/LocalBeats.png b/LocalBeats.png new file mode 100644 index 00000000..27078ef6 Binary files /dev/null and b/LocalBeats.png differ diff --git a/dist/images/3a426ca7e83eb4d81d6e.png b/dist/images/3a426ca7e83eb4d81d6e.png new file mode 100644 index 00000000..665a4490 Binary files /dev/null and b/dist/images/3a426ca7e83eb4d81d6e.png differ diff --git a/dist/images/ef3dfda9668f59f80dbd.png b/dist/images/ef3dfda9668f59f80dbd.png new file mode 100644 index 00000000..27078ef6 Binary files /dev/null and b/dist/images/ef3dfda9668f59f80dbd.png differ diff --git a/dist/index.html b/dist/index.html index e864e00a..9ccd78c6 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,13 +1,19 @@ - - - - - Capstone 1 - - - -
- - + + + + + + + + + Capstone 1 + + + + +
+ + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index dc268f09..fd365a2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,17 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@auth0/auth0-react": "^2.3.0", + "@auth0/auth0-react": "^2.4.0", "@babel/core": "^7.27.4", "@babel/preset-react": "^7.27.1", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.1", + "@mui/material": "^7.3.1", "axios": "^1.10.0", "babel-loader": "^10.0.0", "dotenv": "^17.1.0", + "jwt-decode": "^4.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.6.3", @@ -45,12 +50,12 @@ } }, "node_modules/@auth0/auth0-react": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.3.0.tgz", - "integrity": "sha512-YYTc/DWWigKC9fURufR/79h3+3DAnIzbfEzJLZ8Z4Q0BXE0azru3pKUbU+vYzS4lMAJkclwLuAbUnLjK81vCpA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.4.0.tgz", + "integrity": "sha512-5bt3sO9FVupNM15IpqyYu/2OPHpLI5El7RgWLQXZOPbnCBbtl+VgdHR+H2NfhNQ4SqQtC/5uKbHWafcVcsxkiw==", "license": "MIT", "dependencies": { - "@auth0/auth0-spa-js": "^2.1.3" + "@auth0/auth0-spa-js": "^2.2.0" }, "peerDependencies": { "react": "^16.11.0 || ^17 || ^18 || ^19", @@ -354,6 +359,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -417,6 +431,167 @@ "node": ">=14.17.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -582,13 +757,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -596,9 +771,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -796,6 +971,261 @@ "dev": true, "license": "MIT" }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.1.tgz", + "integrity": "sha512-+mIK1Z0BhOaQ0vCgOkT1mSrIpEHLo338h4/duuL4TBLXPvUMit732mnwJY3W40Avy30HdeSfwUAAGRkKmwRaEQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.1.tgz", + "integrity": "sha512-upzCtG6awpL6noEZlJ5Z01khZ9VnLNLaj7tb6iPbN6G97eYfUTs8e9OyPKy3rEms3VQWmVBfri7jzeaRxdFIzA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.3.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.1.tgz", + "integrity": "sha512-Xf6Shbo03YmcBedZMwSpEFOwpYDtU7tC+rhAHTrA9FHk0FpsDqiQ9jUa1j/9s3HLs7KWb5mDcGnlwdh9Q9KAag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/core-downloads-tracker": "^7.3.1", + "@mui/system": "^7.3.1", + "@mui/types": "^7.4.5", + "@mui/utils": "^7.3.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", + "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.1.tgz", + "integrity": "sha512-WU3YLkKXii/x8ZEKnrLKsPwplCVE11yZxUvlaaZSIzCcI3x2OdFC8eMlNy74hVeUsYQvzzX1Es/k4ARPlFvpPQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/utils": "^7.3.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.1.tgz", + "integrity": "sha512-Nqo6OHjvJpXJ1+9TekTE//+8RybgPQUKwns2Lh0sq+8rJOUSUKS3KALv4InSOdHhIM9Mdi8/L7LTF1/Ky6D6TQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.1.tgz", + "integrity": "sha512-mIidecvcNVpNJMdPDmCeoSL5zshKBbYPcphjuh6ZMjhybhqhZ4mX6k9zmIWh6XOXcqRQMg5KrcjnO0QstrNj3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/private-theming": "^7.3.1", + "@mui/styled-engine": "^7.3.1", + "@mui/types": "^7.4.5", + "@mui/utils": "^7.3.1", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.5.tgz", + "integrity": "sha512-ZPwlAOE3e8C0piCKbaabwrqZbW4QvWz0uapVPWya7fYj6PeDkl5sSJmomT7wjOcZGPB48G/a6Ubidqreptxz4g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.1.tgz", + "integrity": "sha512-/31y4wZqVWa0jzMnzo6JPjxwP6xXy4P3+iLbosFg/mJQowL1KIou0LC+lquWW60FKVbKz5ZUWBg2H3jausa0pw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/types": "^7.4.5", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", + "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -939,6 +1369,18 @@ "@types/node": "*" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", @@ -953,6 +1395,25 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/react": { + "version": "19.1.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz", + "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", @@ -1560,6 +2021,21 @@ "webpack": ">=5.61.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1781,7 +2257,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -1885,6 +2360,15 @@ "node": ">=6" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1943,9 +2427,9 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "license": "MIT", "dependencies": { @@ -1953,7 +2437,7 @@ "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -2048,6 +2532,22 @@ "dev": true, "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2124,6 +2624,12 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2344,6 +2850,16 @@ "node": ">=0.10.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dotenv": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.1.0.tgz", @@ -2418,6 +2934,15 @@ "node": ">=4" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -2617,7 +3142,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3129,6 +3653,12 @@ "dev": true, "license": "MIT" }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3212,9 +3742,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -3530,6 +4060,15 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -3697,7 +4236,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -3714,7 +4252,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -3808,6 +4345,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -4421,6 +4964,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4465,6 +5017,12 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -4500,7 +5058,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4743,7 +5300,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4868,9 +5424,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, "license": "MIT", "engines": { @@ -4993,7 +5549,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -5002,6 +5557,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -5043,6 +5616,15 @@ "dev": true, "license": "MIT" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5270,7 +5852,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -5394,7 +5975,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-router": { @@ -5444,6 +6024,22 @@ "node": ">=18" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6341,6 +6937,12 @@ "webpack": "^5.27.0" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -7125,6 +7727,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index da1486c6..dadfe119 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,17 @@ "license": "ISC", "description": "", "dependencies": { - "@auth0/auth0-react": "^2.3.0", + "@auth0/auth0-react": "^2.4.0", "@babel/core": "^7.27.4", "@babel/preset-react": "^7.27.1", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.1", + "@mui/material": "^7.3.1", "axios": "^1.10.0", "babel-loader": "^10.0.0", "dotenv": "^17.1.0", + "jwt-decode": "^4.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-router-dom": "^7.6.3", diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 00000000..a314507d Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/App.jsx b/src/App.jsx index d793b9af..73b4a3ae 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,71 +1,159 @@ import React, { useState, useEffect } from "react"; import { createRoot } from "react-dom/client"; -import axios from "axios"; +// import axios from "axios"; import "./AppStyles.css"; import NavBar from "./components/NavBar"; +import Dashboard from "./components/Dashboard"; +// import NowPlaying from "./components/Activelistener"; +// import ActliveListener from "./components/Activelistener"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import Login from "./components/Login"; +import LandingPage from "./components/LandingPage"; import Signup from "./components/Signup"; -import Home from "./components/Home"; +// import Home from "./components/Home"; import NotFound from "./components/NotFound"; -import { API_URL } from "./shared"; +// import { jwtDecode } from "jwt-decode"; +// import { API_URL } from "./shared"; +import axios from './utils/axiosInstance'; +import { Auth0Provider, useAuth0 } from "@auth0/auth0-react"; +//test + +// AUTH0 CONFIGURATION +const AUTH0_DOMAIN = process.env.REACT_APP_AUTH0_DOMAIN; +const AUTH0_CLIENT_ID = process.env.REACT_APP_AUTH0_CLIENT_ID; const App = () => { const [user, setUser] = useState(null); + console.log("this is user--->", user) + //const [token, setToken] = useState(""); - const checkAuth = async () => { - try { - const response = await axios.get(`${API_URL}/auth/me`, { - withCredentials: true, - }); - setUser(response.data.user); - } catch { - console.log("Not authenticated"); - setUser(null); - } - }; + const { + isAuthenticated, + user: auth0User, + getIdTokenClaims, + } = useAuth0(); // swapped getAccessTokenSilently for getIdTokenClaims - // Check authentication status on app load useEffect(() => { + const checkAuth = async () => { + try { + const response = await axios.get("/auth/me", { + withCredentials: true, + }); + setUser(response.data.user); + } catch { + console.log("Not authenticated"); + setUser(null); + } + }; checkAuth(); }, []); - const handleLogout = async () => { - try { - // Logout from our backend - await axios.post( - `${API_URL}/auth/logout`, - {}, - { + useEffect(() => { + const syncSpotifyAndFetchUser = async () => { + if (!isAuthenticated || !auth0User) { + console.warn("User not authenticated or auth0User not ready."); + return; + } + + try { + const claims = await getIdTokenClaims(); + const spotifyAccessToken = claims["https://localbeats.app/spotify_access_token"]; + + if (!spotifyAccessToken) { + console.warn("No Spotify access token found in ID token claims."); + return; + } + + // Sync with backend — this will update DB & create session token + await axios.post("/auth/spotify/sync", {}, { + headers: { + Authorization: `Bearer ${spotifyAccessToken}`, + }, withCredentials: true, } - ); + ); + + + // Fetch user info from DB (using session token) + const res = await axios.get("/auth/me", { withCredentials: true }); + console.log("this is data-->", res.data) + setUser(res.data.user); + } catch (err) { + console.error("Post-login sync failed:", err); + } + }; + + // Update frontend state + // setUser({ + // name: auth0User.name, + // email: auth0User.email, + // picture: auth0User.picture, + // }); + + console.log("Auth0 state ->", { isAuthenticated, auth0User }); + syncSpotifyAndFetchUser(); + }, [isAuthenticated, auth0User, getIdTokenClaims]); + + + + const handleLogout = async () => { + try { + await axios.post('/auth/logout', {}, { withCredentials: true }); setUser(null); + // Redirect to Auth0 logout, which will also redirect to your landing page + window.location.href = `https://${AUTH0_DOMAIN}/v2/logout?client_id=${AUTH0_CLIENT_ID}&returnTo=${encodeURIComponent(window.location.origin + "/")}`; } catch (error) { console.error("Logout error:", error); } }; return ( -
- -
- - } /> - } /> - } /> - } /> - -
+
+ + } /> + } /> + + Loading your profile... +
+ ) : ( + <> + + + + ) + } /> + } /> +
); }; +// ✅ Root with Spotify audience + scopes const Root = () => { return ( - - - + // + + + + + ); }; diff --git a/src/AppStyles.css b/src/AppStyles.css index 765150b1..c24c1db8 100644 --- a/src/AppStyles.css +++ b/src/AppStyles.css @@ -1,12 +1,44 @@ +/* +.landing-title { + color: #fff; + font-size: 3.45rem; +} + +.landing-subtitle { + color: #fff; + font-size: 1.725rem; +} + +@media (max-width: 600px) { + .landing-title { + font-size: 3.243rem; + text-align: center; + width: 100vw; + display: block; + } + .landing-subtitle { + text-align: center; + width: 100vw; + display: block; + } +} +@import url('https://fonts.googleapis.com/css?family=Rubik:900&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; - font-family: "Raleway", sans-serif; + font-family: 'Rubik', sans-serif; } body { - background-color: #fff; + background: url('./assets/amy-background2.png') no-repeat center center fixed; + background-size: cover; + background-attachment: fixed; + min-height: 100vh; + min-width: 100vw; + width: 100vw; + height: 100vh; + overflow-x: hidden; } .title { @@ -33,9 +65,4 @@ body { transform: rotate(360deg); } } - -.react-logo { - width: 100px; - height: 100px; - animation: spin 15s linear infinite; -} +*/ diff --git a/src/assets/Beat-Nav.png b/src/assets/Beat-Nav.png new file mode 100644 index 00000000..89473e02 Binary files /dev/null and b/src/assets/Beat-Nav.png differ diff --git a/src/assets/Local-Beats.png b/src/assets/Local-Beats.png new file mode 100644 index 00000000..eacf9174 Binary files /dev/null and b/src/assets/Local-Beats.png differ diff --git a/src/assets/LocalBeats.png b/src/assets/LocalBeats.png new file mode 100644 index 00000000..27078ef6 Binary files /dev/null and b/src/assets/LocalBeats.png differ diff --git a/src/assets/amy-background2.png b/src/assets/amy-background2.png new file mode 100644 index 00000000..2c99bfed Binary files /dev/null and b/src/assets/amy-background2.png differ diff --git a/src/assets/spotify-logo.png b/src/assets/spotify-logo.png new file mode 100644 index 00000000..f291809e Binary files /dev/null and b/src/assets/spotify-logo.png differ diff --git a/src/components/ActiveListener.css b/src/components/ActiveListener.css new file mode 100644 index 00000000..d1f97a50 --- /dev/null +++ b/src/components/ActiveListener.css @@ -0,0 +1,67 @@ +.active-listener-container{ + /* flex create a box, easy to manage */ + display: flex; + flex-direction:column; + gap: 2rem; +} + +.active-listener-card{ + +} + +.listener-card-container { + display: flex; + border-style: solid; + border-color: black; + border-radius: 1rem; + border-width: 2px; + gap: .9rem; + align-items: center; + /* align-content: center; */ + background-color: lightgreen; + /* justify-content: space-around; */ + padding-left:10px; + padding-right:1px; +} + +.listener-card-status { + border-style: solid; + border-radius: .5rem; + box-sizing: border-box; + padding: .3rem; +} + +.listener-card-album-art { + border-radius:.5rem; + min-height: 50px; + max-width: 50px; +} + +.listener-card-name { + margin:5px 0px; + font-family: none; + font-size:small; +} + +.listener-card-song { + margin:1px 0px; + font-family: none; + font-weight: bold; +} + +.listener-card-artist { + margin:1px 0px; + font-family: none; + font-size:small; +} + +.listener-card-spotify { + height:30px; + width: 30px; +} + +.listener-card-spotify-container { + position:absolute; + left:88%; +} + diff --git a/src/components/ActiveListener.jsx b/src/components/ActiveListener.jsx new file mode 100644 index 00000000..c290b0c7 --- /dev/null +++ b/src/components/ActiveListener.jsx @@ -0,0 +1,243 @@ +import React, { useState, useEffect, useRef } from "react"; +import axios from "../utils/axiosInstance"; +import ListenerCard from "./ListenerCard"; +import spotifyLogo from "../assets/spotify-logo.png"; + +const ActiveListener = ({ user }) => { + console.log("this is user--->", user); + // console.log("this is user from Nowplaying--->", user) + const [track, setTrack] = useState(null); + console.log("this is track--->", track); + const [error, setError] = useState(null); + + const [activeSession, setActiveSession] = useState(null); + // console.log("This is active session", activeSession) + + const [allListeningSessions, setAllListeningSessions] = useState([]); + console.log("This is All Listening Sessions", allListeningSessions); + + const lastSongIdRef = useRef(null); // last seen song_id from polling + const openSessionIdRef = useRef(null); // DB id of the currently-open session + const isSyncingRef = useRef(false); // simple lock to prevent overlap + let aliveRef = useRef(true); + + // oldSessionRef.current = null + // console.log("this is old session ref--->", oldSessionRef.current) + + useEffect(() => { + const fetchCurrentTrack = async () => { + try { + const { data } = await axios.get("/api/spotify/current-track"); + + if (!aliveRef.current) return; + + // Only set track if data is valid and has a title + if (data?.title) { + // only change state of track if we get a new track + if (data.song_id !== lastSongIdRef.current) { + setTrack(data); + lastSongIdRef.current = data.song_id; + } + setError(null); + } else { + // no song playing now + if (lastSongIdRef.current !== null) { + lastSongIdRef.current = null; + setTrack(null); + } + } + } catch (err) { + if (!aliveRef.current) return; + console.error("Error fetching current track:", err); + setError("Unable to fetch currently playing track."); + } + }; + + // Initial fetch + interval + fetchCurrentTrack(); + const intervalId = setInterval(fetchCurrentTrack, 10000); // Refresh every 10s + + // Cleanup: stop interval + return () => { + aliveRef.current = false; + clearInterval(intervalId); + }; + }, []); + + // + // Sync a session whenever song_id OR user changes + // - If no track -> stop existing session (if any) and clear ref + // - If track -> stop old (if any), then create a new session + // + + useEffect(() => { + console.log("Entered useEffect for syncListeningSession"); + const syncListeningSession = async () => { + // console.log("This is user ID --->", user.id) + if (!user?.id) return; + if (isSyncingRef.current) return; + isSyncingRef.current = true; + try { + // if there is no track playing stop and clear then leave and do nothing + if (!track) { + if (openSessionIdRef.current) { + console.log("ending any pending session"); + await axios.patch( + "/api/listeners", + { + id: openSessionIdRef.current, + status: "stopped", + }, + { + withCredentials: true, + } + ); + openSessionIdRef.current = null; + setActiveSession(null); + isSyncingRef.current = false; + } + return; + } + + // if track exist end old session first if any on memory + if (openSessionIdRef.current) { + console.log("found track closing old"); + await axios.patch("/api/listeners", { + status: "stopped", + id: openSessionIdRef.current, + }); + } + + // create a new session for this track + const newListeningSession = await axios.post("/api/listeners", { + status: "playing", + song_id: track.song_id, + ended_at: null, + }); + + setActiveSession(newListeningSession.data); + openSessionIdRef.current = newListeningSession.data.id; + } catch (error) { + console.log(error?.response?.data || error.message || error); + } finally { + isSyncingRef.current = false; + } + }; + syncListeningSession(); + }, [track?.song_id, user?.id]); + + // end sessin on unmount + useEffect(() => { + return () => { + if (openSessionIdRef.current) { + axios + .patch("/api/listeners", { + id: openSessionIdRef.current, + status: "stopped", + }) + .catch(() => {}); + } + }; + }, []); + + const fetchAllActiveListeners = React.useCallback(async () => { + try { + const res = await axios.get("/api/listeners", { withCredentials: true }); + // if (!aliveRef.current) return; + + setAllListeningSessions((prev) => { + if (prev.length !== res.data.length) return res.data; + const prevIds = prev + .map((x) => x.id) + .sort() + .join(","); + const nextIds = res.data + .map((x) => x.id) + .sort() + .join(","); + return prevIds === nextIds ? prev : res.data; + }); + } catch (err) { + console.error("Error fetching active listeners:", err); + } + }, []); + + //keeps others’ updates flowing in + useEffect(() => { + aliveRef.current = true; + + fetchAllActiveListeners(); // immediate + const intervalId = setInterval(fetchAllActiveListeners, 8000); + + const onVisible = () => { + if (document.visibilityState === "visible") fetchAllActiveListeners(); + }; + document.addEventListener("visibilitychange", onVisible); + + return () => { + aliveRef.current = false; + clearInterval(intervalId); + document.removeEventListener("visibilitychange", onVisible); + }; + }, [fetchAllActiveListeners]); + + //Instant refresh when change sessions + useEffect(() => { + if (!activeSession?.id) return; + fetchAllActiveListeners(); + }, [activeSession?.id, fetchAllActiveListeners]); + + // if (error) + // return

{error}

; + // if (!track) + // return ( + //

+ // No song is currently playing. + //

+ // ); + + return ( +
+

Active Listeners

+ {/* Current user */} + {/* */} + {/* Other active listeners */} +
+ {allListeningSessions?.map((session) => ( + + ))} +
+ +
+
+ + {/* Profile */} +
+
+

Local Beats

+

Le Onde

+

Ludovico Einaudi

+ {/*

{loc}

*/} +
+
+ + + +
+
+
+ ); +}; + +export default ActiveListener; diff --git a/src/components/AuthStyles.css b/src/components/AuthStyles.css index 0506f6ce..3813d415 100644 --- a/src/components/AuthStyles.css +++ b/src/components/AuthStyles.css @@ -1,5 +1,63 @@ +/* Animated partial purple ring for Spotify button hover */ +.spotify-login-btn { + position: relative; + outline: none; +} + +.spotify-login-btn img { + transition: transform 0.2s; + border-radius: 50%; + z-index: 1; +} + +.spotify-login-btn::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 72px; + height: 72px; + border-radius: 50%; + border: 3px solid transparent; + border-top: 3px solid #a259f7; + border-right: 3px solid #a259f7; + transform: translate(-50%, -50%) rotate(0deg); + opacity: 0; + pointer-events: none; + z-index: 2; +} + +.spotify-login-btn:hover img { + transform: scale(1.2); +} + +.spotify-login-btn:hover::after { + opacity: 1; + animation: spin-ring 1s linear infinite; +} + +@keyframes spin-ring { + 0% { + transform: translate(-50%, -50%) rotate(0deg); + } + 100% { + transform: translate(-50%, -50%) rotate(360deg); + } +} +/* Spotify logo button hover effect */ +.spotify-login-btn img { + transition: transform 0.2s, box-shadow 0.2s; + border-radius: 50%; +} + +.spotify-login-btn:hover img { + transform: scale(1.2); + box-shadow: 0 0 0 5px #a259f7; +} .auth-container { display: flex; + flex-direction: column; + gap: 5rem; justify-content: center; align-items: center; min-height: 60vh; @@ -7,23 +65,14 @@ } .auth-form { - background: white; - padding: 2rem; - border-radius: 8px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - width: 100%; - max-width: 400px; + background: none; + padding: 0; + border-radius: 0; + box-shadow: none; + width: auto; + max-width: none; } -.auth-form h2 { - text-align: center; - margin-bottom: 1.5rem; - color: #333; -} - -.form-group { - margin-bottom: 1rem; -} .form-group label { display: block; @@ -143,4 +192,4 @@ .auth-link a:hover { text-decoration: underline; -} +} \ No newline at end of file diff --git a/src/components/Dashboard.css b/src/components/Dashboard.css new file mode 100644 index 00000000..d1afc391 --- /dev/null +++ b/src/components/Dashboard.css @@ -0,0 +1,274 @@ +/* Dashboard.css - styles for Dashboard component only */ + + +.dashboard-main { + position: relative; + min-height: 100vh; + background: #fff; +} + + +.dashboard-title { + text-align: center; + font-size: 2rem; + font-weight: 700; + margin: 0; + padding: 1.2rem 0 0.5rem 0; + letter-spacing: 0.02em; +} + +.dashboard-location-box { + position: absolute; + top: 20px; + right: -270px; + background: #f5f5f5; + border: 1px solid #ddd; + border-radius: 8px; + padding: 12px 18px; + min-width: 160px; + max-width: 260px; + box-shadow: 0 2px 8px rgba(0,0,0,0.08); + font-size: 14px; + z-index: 10; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + gap: 8px; + word-break: break-word; + overflow-wrap: break-word; +} + +.dashboard-location-box strong { + font-weight: bold; +} + +.dashboard-location-box .dashboard-address { + color: #333; + font-weight: 500; + word-break: break-word; + white-space: pre-line; +} + +.dashboard-location-box .dashboard-error { + color: #c00; +} + + +/* Map container: mobile first, covers full viewport */ + +/* + === MAP DIMENSIONS (Mobile) === + To adjust the map height/width on mobile, edit the values below. + Example: height: 70vh; (for 70% of viewport height) +*/ +/* + === MAP VERTICAL POSITION (Mobile) === + To move the map higher or lower on mobile, edit the 'top' value below. + Example: top: 60px; (moves map closer to navbar) +*/ +/* + === MAP HEIGHT (Mobile) === + To make the map extend lower, increase the height below. + Example: height: 700px; (makes map container and map taller) +*/ +.dashboard-map-container { + position: fixed; + top: 106px; /* <-- MAP TOP OFFSET (Mobile) */ + left: 0; + width: 100vw; + height: 650px; /* <-- MAP HEIGHT (Mobile, container) */ + margin: 0; + border: none; + border-radius: 0; + box-shadow: none; + overflow: hidden; + background: #eaeaea; + display: flex; + align-items: flex-end; + justify-content: center; + z-index: 100; +} + + +/* + To adjust the map itself (not the container), edit below. +*/ +/* + To make the map itself extend lower, increase the height below. +*/ +.dashboard-map { + width: 100vw; + height: 635px; /* <-- MAP HEIGHT (Mobile, actual map) */ + position: absolute; + top: 0; + left: 0; +} + +/* Bubble button at bottom center */ +.dashboard-bubble-btn { + position: fixed; + left: 50%; + bottom: 15px; + transform: translateX(-50%); + background: rgb(46, 45, 46); + color:lightgreen; + /* display: block; */ + /* margin: 0 auto 1.2rem auto; */ + font-weight: 600; + font-size: 1rem; + border: none; + border-radius: 999px; + /* box-shadow: 0 2px 12px rgba(25, 118, 210, 0.18); */ + padding: 0.45em 1.3em; + cursor: pointer; + transition: background 0.2s, color 0.2s; + z-index: 101; + display: flex; + align-items: center; + gap: 0.2em; +} +.dashboard-bubble-btn:active { + background: #e3f2fd; + color: #0d47a1; + align-items: center; + gap:0.5em; +} + +/* Results section: covers full viewport, scrollable */ +.dashboard-results-section { +/* Back to Map button at top of results section */ +/* Center the Back to Map button in results section */ +.dashboard-back-btn { + position: fixed; + bottom:1px; + left:50%; + transform:translateX(-50%); + /* aligns horital in the middle */ + margin: 0 auto 1.2rem auto; + background: rgb(46, 45, 46); + color:lightgreen; + font-weight: 600; + font-size: 1rem; + border: none; + border-radius: 999px; + /* box-shadow: 0 1px 6px rgba(25, 118, 210, 0.10); */ + padding: 0.45em 1.3em; + cursor: pointer; + /* z-index removed to prevent hovering above navbar */ + transition: background 0.18s, color 0.18s, border 0.18s; + display: flex; + align-items: center; + gap: 0.2em; +} +.dashboard-back-btn:active { + background: #e3f2fd; + color: #0d47a1; + border-color: #0d47a1; +} +/* Results section: covers full viewport, scrollable */ +.dashboard-results-section { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: #fff; + z-index: 200; + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 2.5rem; + padding-bottom: 2.5rem; + box-sizing: border-box; +} + +/* Header container for results section */ +.dashboard-results-header { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 1.2rem; +} + +.dashboard-header-texts { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 0.5rem; +} + +.dashboard-title-results { + margin-bottom: 0.5rem; + /* To move the Dashboard title closer to the Back to Map button, decrease margin-top below. */ + /* Example: margin-top: 0.5rem; (default) To bring it up by 12px, use margin-top: -12px; */ + margin-top: -34px; +} +} +.dashboard-results-title { + text-align: center; + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 1.2rem; + margin-top: 0; + color: #1976d2; +} + + +@media (min-width: 600px) { + .dashboard-title { + font-size: 2.5rem; + } + /* + === MAP DIMENSIONS (Desktop) === + To adjust the map height/width on desktop, edit the values below. + Example: width: 800px; height: 500px; + */ + .dashboard-map-container { + width: 700px; /* <-- EDIT THIS for desktop map width */ + height: 350px; /* <-- EDIT THIS for desktop map height */ + position: relative; + border: 2px solid #333; + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.12); + margin: 40px auto; + left: unset; + top: unset; + z-index: 10; + } + .dashboard-map { + width: 100%; + height: 100%; + position: static; + } + .dashboard-bubble-btn { + bottom: 16px; + } + .dashboard-results-section { + width: 100vw; + height: 100vh; + max-width: 700px; + left: 50%; + transform: translateX(-50%); + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.12); + margin: 40px auto; + } + .dashboard-location-box { + display: flex; + } +} + +@media (max-width: 599px) { + .dashboard-location-box { + display: none; + } +} + +/* .css-1umw9bq-MuiSvgIcon-root { + display: flex; + align-items: center; + gap: 0.5em; +} */ diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx new file mode 100644 index 00000000..4bd2a723 --- /dev/null +++ b/src/components/Dashboard.jsx @@ -0,0 +1,266 @@ +import React, { useEffect, useState, useRef } from "react"; +import axios from "../utils/axiosInstance"; +import ActiveListener from "./ActiveListener"; +import NowPlaying from "./NowPlaying"; +import LocalBeatsImg from "../assets/LocalBeats.png"; +import "./Dashboard.css"; +import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted"; +import MapIcon from "@mui/icons-material/Map"; + +const apiKey = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; + +function loadGoogleMapsScript(apiKey, callback) { + if (window.google && window.google.maps) { + callback(); + return; + } + const existingScript = document.getElementById("google-maps-script"); + if (!existingScript) { + const script = document.createElement("script"); + script.id = "google-maps-script"; + script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`; + script.async = true; + script.onload = callback; + script.onerror = function (e) { + console.error("Failed to load Google Maps script", e); + alert("Failed to load Google Maps. Check your API key and network."); + }; + document.body.appendChild(script); + } else { + existingScript.onload = callback; + } +} + +const Dashboard = ({ user }) => { + if (!user) return null; + + const [coords, setCoords] = useState(null); + const [geoError, setGeoError] = useState(null); + const [address, setAddress] = useState(""); + const [mapKey, setMapKey] = useState(0); + const mapRef = useRef(null); + const [onlineUsers, setOnlineUsers] = useState([]); + const [showResults, setShowResults] = useState(false); + const mapInstanceRef = useRef(null); + const markersRef = useRef([]); + + // Get current location and post to backend + useEffect(() => { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + async (position) => { + const lat = position.coords.latitude; + const lng = position.coords.longitude; + setCoords({ lat, lng }); + setGeoError(null); + + // Reverse geocode + fetch( + `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${apiKey}` + ) + .then((res) => res.json()) + .then((data) => { + if (data.status === "OK" && data.results.length > 0) { + setAddress(data.results[0].formatted_address); + } else { + setAddress(""); + } + }) + .catch(() => setAddress("")); + + // Update backend with location + try { + await axios.post( + "/api/users/location", + { latitude: lat, longitude: lng }, + { withCredentials: true } + ); + } catch (err) { + console.error("Failed to update location:", err); + } + }, + (error) => { + setGeoError(error.message); + } + ); + } else { + setGeoError("Geolocation is not supported by this browser."); + } + }, []); + + // Function to fetch online users + const fetchOnlineUsers = async () => { + try { + const res = await axios.get("/api/users/online", { + withCredentials: true, + }); + setOnlineUsers(res.data.users || []); + } catch (err) { + console.error("Failed to fetch online users:", err); + } + }; + + // Poll online users every 10 seconds + useEffect(() => { + if (user) { + fetchOnlineUsers(); + const interval = setInterval(fetchOnlineUsers, 10000); + return () => clearInterval(interval); + } + }, [user]); + + // Load map once coords are set OR when mapKey changes + useEffect(() => { + if (coords && apiKey && mapRef.current) { + loadGoogleMapsScript(apiKey, () => { + if (window.google && window.google.maps) { + const customMapStyle = [ + { + featureType: "poi", + elementType: "labels.icon", + stylers: [{ visibility: "off" }], + }, + { + featureType: "transit", + elementType: "labels.icon", + stylers: [{ visibility: "off" }], + }, + ]; + + mapInstanceRef.current = new window.google.maps.Map(mapRef.current, { + center: coords, + zoom: 12, + mapTypeControl: false, + streetViewControl: false, + fullscreenControl: false, + zoomControl: false, + styles: customMapStyle, + }); + + // NYC borough polygon + const nycBoroughsCoords = [ + { lat: 40.917577, lng: -73.700272 }, + { lat: 40.915255, lng: -73.786137 }, + { lat: 40.849255, lng: -73.786137 }, + { lat: 40.5774, lng: -73.8371 }, + { lat: 40.5116, lng: -74.2556 }, + { lat: 40.639722, lng: -74.081667 }, + { lat: 40.8007, lng: -74.0256 }, + { lat: 40.917577, lng: -73.700272 }, + ]; + new window.google.maps.Polygon({ + paths: nycBoroughsCoords, + strokeColor: "#2196f3", + strokeOpacity: 0.6, + strokeWeight: 2, + fillColor: "#90caf9", + fillOpacity: 0.25, + map: mapInstanceRef.current, + }); + } + }); + } + }, [coords, apiKey, mapKey]); + + // Update markers whenever onlineUsers changes or map reloads + useEffect(() => { + if (mapInstanceRef.current) { + // Clear old markers + markersRef.current.forEach((marker) => marker.setMap(null)); + markersRef.current = []; + + // Add markers for users + onlineUsers.forEach((u) => { + if (typeof u.latitude === "number" && typeof u.longitude === "number") { + const isCurrentUser = u.username === user.username; + + const markerOptions = { + position: { lat: u.latitude, lng: u.longitude }, + map: mapInstanceRef.current, + icon: isCurrentUser + ? { + url: LocalBeatsImg, + scaledSize: new window.google.maps.Size(40, 40), + } + : { + url: "https://maps.google.com/mapfiles/ms/icons/green-dot.png", + scaledSize: new window.google.maps.Size(40, 40), + }, + }; + + const marker = new window.google.maps.Marker(markerOptions); + markersRef.current.push(marker); + } + }); + } + }, [onlineUsers, mapKey, user]); + + return ( +
+ {!showResults &&

Dashboard

} + + {user && !showResults && ( +
+ Your Location +
+ {coords ? ( + address ? ( +
{address}
+ ) : ( +
Fetching address...
+ ) + ) : geoError ? ( +
{geoError}
+ ) : ( +
Fetching location...
+ )} +
+
+ )} + + {user && coords && !showResults && ( +
+
+ +
+ )} + + {showResults && ( +
+
+ +
+ {/*

Dashboard

+

Your Currently Playing:

*/} +
+
+ {/* */} + +
+ )} +
+ ); +}; + +export default Dashboard; diff --git a/src/components/Home.jsx b/src/components/Home.jsx deleted file mode 100644 index 130ea9eb..00000000 --- a/src/components/Home.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; - -const Home = () => { - return ( - <> -

Hello React!

- React Logo - - ); -}; - -export default Home; diff --git a/src/components/LandingPage.css b/src/components/LandingPage.css new file mode 100644 index 00000000..8f400f7b --- /dev/null +++ b/src/components/LandingPage.css @@ -0,0 +1,105 @@ + +/* + --logo-size: Controls the width of the LocalBeats logo (desktop) + --logo-height: Controls the height of the LocalBeats logo (desktop) + --logo-size-mobile: Controls the width of the LocalBeats logo (mobile) + --logo-height-mobile: Controls the height of the LocalBeats logo (mobile) + Edit these values below to resize the logo as needed. +*/ +:root { + --logo-size: 160px; /* <-- Edit for desktop width */ + --logo-height: 160px; /* <-- Edit for desktop height */ + --logo-size-mobile: 140px; /* <-- Edit for mobile width */ + --logo-height-mobile: 140px; /* <-- Edit for mobile height */ +} + +.localbeats-main-logo { + display: block; + margin: -60px auto 48px auto; /* Moved up by 60px, 48px gap below logo (edit for desktop gap) */ + width: var(--logo-size); + height: var(--logo-height); + /* To move logo up/down, adjust margin-top. To change gap below logo, edit margin-bottom. */ +} + +@media (max-width: 600px) { + .localbeats-main-logo { + width: var(--logo-size-mobile); + height: var(--logo-height-mobile); + margin: -30px auto 32px auto; /* Moved up by 30px, 32px gap below logo (edit for mobile gap) */ + } +} +@import url('https://fonts.googleapis.com/css?family=Rubik:900&display=swap'); + +body, .landing-spotify-container, .landing-headline, .login-title, .spotify-logo-btn { + font-family: 'Rubik', sans-serif !important; +} + +.landing-headline { + color: #fff; + font-size: 44px; + font-weight: 900; + margin-bottom: 18px; + text-align: center; + text-shadow: 0 2px 8px rgba(0,0,0,0.25); + letter-spacing: 0.5px; + line-height: 1.15; +} + +@media (max-width: 600px) { + .landing-headline { + font-size: 45px; + margin-bottom: 14px; + } + .login-title { + font-size: 20px; + margin-bottom: 22px; + } +} + +.landing-spotify-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + width: 100vw; + position: fixed; + top: 0; + left: 0; + z-index: 10; +} + +.login-title { + color: #fff; + font-size: 28px; + font-weight: 700; + margin-bottom: 32px; + text-shadow: 0 2px 8px rgba(0,0,0,0.25); + letter-spacing: 0.5px; +} + +.spotify-logo-btn { + background: transparent; + border: none; + outline: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 24px rgba(30,215,96,0.18), 0 1.5px 8px rgba(0,0,0,0.10); + border-radius: 50%; + transition: transform 0.15s, box-shadow 0.15s; + margin-bottom: 0; +} + +.spotify-logo-btn:hover { + transform: scale(1.08); + box-shadow: 0 8px 32px rgba(30,215,96,0.28), 0 2px 12px rgba(0,0,0,0.15); +} + +.spotify-logo-img { + width: 80px; + height: 80px; + object-fit: contain; + display: block; +} diff --git a/src/components/LandingPage.jsx b/src/components/LandingPage.jsx new file mode 100644 index 00000000..f78f5d5e --- /dev/null +++ b/src/components/LandingPage.jsx @@ -0,0 +1,75 @@ +import React, { useEffect } from "react"; +import axios from "../utils/axiosInstance"; +import { useNavigate } from "react-router-dom"; +import { useAuth0 } from "@auth0/auth0-react"; +import "./AuthStyles.css"; +import "./LandingPage.css"; + +const LandingPage = ({ setUser }) => { + const { loginWithRedirect, isAuthenticated, user: auth0User } = useAuth0(); + const navigate = useNavigate(); + + // Set amy-background2.png as background only on this page + useEffect(() => { + const originalBg = document.body.style.background; + document.body.style.background = `url(${require("../assets/amy-background2.png")}) no-repeat center center fixed`; + document.body.style.backgroundSize = "cover"; + document.body.style.backgroundAttachment = "fixed"; + return () => { + document.body.style.background = originalBg; + document.body.style.backgroundSize = ""; + document.body.style.backgroundAttachment = ""; + }; + }, []); + + useEffect(() => { + const verifySessionAndSetUser = async () => { + if (isAuthenticated && auth0User) { + try { + const res = await axios.get("/auth/me"); + if (res.data.user) { + setUser(res.data.user); + navigate("/"); + } else { + console.warn("No user returned from /auth/me"); + } + } catch (error) { + console.error("Error verifying session:", error.response?.data || error.message); + } + } + }; + verifySessionAndSetUser(); + }, [isAuthenticated, auth0User, setUser, navigate]); + + return ( +
+ LocalBeats Logo +
Discover What NYC is Vibing To!
+
Login with Spotify
+ +
+ ); +}; + +export default LandingPage; diff --git a/src/components/ListenerCard.css b/src/components/ListenerCard.css new file mode 100644 index 00000000..d2cf5017 --- /dev/null +++ b/src/components/ListenerCard.css @@ -0,0 +1,56 @@ +.listener-card-container { + display: flex; + border-style: solid; + border-color: black; + border-radius: 1rem; + border-width: 2px; + gap: .9rem; + align-items: center; + /* align-content: center; */ + background-color: lightgreen; + /* justify-content: space-around; */ + padding-left:10px; + padding-right:1px; +} + +.listener-card-status { + border-style: solid; + border-radius: .5rem; + box-sizing: border-box; + padding: .3rem; +} + +.listener-card-album-art { + border-radius:.5rem; + min-height: 50px; + max-width: 50px; +} + +.listener-card-name { + margin:5px 0px; + font-family: none; + font-size:small; +} + +.listener-card-song { + margin:1px 0px; + font-family: none; + font-weight: bold; +} + +.listener-card-artist { + margin:1px 0px; + font-family: none; + font-size:small; +} + +.listener-card-spotify { + height:30px; + width: 30px; +} + +.listener-card-spotify-container { + position:absolute; + left:88%; +} + diff --git a/src/components/ListenerCard.jsx b/src/components/ListenerCard.jsx new file mode 100644 index 00000000..e30aff63 --- /dev/null +++ b/src/components/ListenerCard.jsx @@ -0,0 +1,40 @@ +import React from "react"; +import logo from "../assets/LocalBeats.png"; +import "./ListenerCard.css"; +import spotifyLogo from "../assets/spotify-logo.png"; + +const ListenerCard = ({ user, track }) => { + console.log("track data:", track); + // Fallbacks for missing data + // const displayName = user?.spotify_display_name || user?.nickname || user?.username || "Unknown"; + // const songTitle = track?.title || "No song playing"; + // const songArtist = track?.artist || ""; + // const songAlbumArt = track?.albumArt || ""; + // const loc = location || user?.location || "Unknown location"; + + return ( +
+
+ + {/* Profile */} +
+
+

{user.spotify_display_name}

+

{track.title}

+

{track.artist}

+ {/*

{loc}

*/} +
+
+ + + +
+
+ ); +}; + +export default ListenerCard; diff --git a/src/components/Login.jsx b/src/components/Login.jsx deleted file mode 100644 index 849e495a..00000000 --- a/src/components/Login.jsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useState } from "react"; -import { useNavigate, Link } from "react-router-dom"; -import axios from "axios"; -import { API_URL } from "../shared"; -import "./AuthStyles.css"; - -const Login = ({ setUser }) => { - const [formData, setFormData] = useState({ - username: "", - password: "", - }); - const [errors, setErrors] = useState({}); - const [isLoading, setIsLoading] = useState(false); - const navigate = useNavigate(); - - const validateForm = () => { - const newErrors = {}; - - if (!formData.username) { - newErrors.username = "Username is required"; - } else if (formData.username.length < 3 || formData.username.length > 20) { - newErrors.username = "Username must be between 3 and 20 characters"; - } - - if (!formData.password) { - newErrors.password = "Password is required"; - } else if (formData.password.length < 6) { - newErrors.password = "Password must be at least 6 characters"; - } - - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - - if (!validateForm()) { - return; - } - - setIsLoading(true); - try { - const response = await axios.post(`${API_URL}/auth/login`, formData, { - withCredentials: true, - }); - - setUser(response.data.user); - navigate("/"); - } catch (error) { - if (error.response?.data?.error) { - setErrors({ general: error.response.data.error }); - } else { - setErrors({ general: "An error occurred during login" }); - } - } finally { - setIsLoading(false); - } - }; - - const handleChange = (e) => { - const { name, value } = e.target; - setFormData((prev) => ({ - ...prev, - [name]: value, - })); - - // Clear error when user starts typing - if (errors[name]) { - setErrors((prev) => ({ - ...prev, - [name]: "", - })); - } - }; - - return ( -
-
-

Login

- - {errors.general && ( -
{errors.general}
- )} - -
-
- - - {errors.username && ( - {errors.username} - )} -
- -
- - - {errors.password && ( - {errors.password} - )} -
- - -
- -

- Don't have an account? Sign up -

-
-
- ); -}; - -export default Login; diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 648f3808..ed27e665 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,33 +1,73 @@ -import React from "react"; +import React, { useState } from "react"; +import { createPortal } from "react-dom"; import { Link } from "react-router-dom"; import "./NavBarStyles.css"; +import logo from '../assets/LocalBeats.png' + const NavBar = ({ user, onLogout }) => { + const [menuOpen, setMenuOpen] = useState(false); + + const handleMenuToggle = () => { + setMenuOpen((prev) => !prev); + }; + + const handleLogout = () => { + setMenuOpen(false); + onLogout(); + }; + return ( ); }; diff --git a/src/components/NavBarStyles.css b/src/components/NavBarStyles.css index e159b511..f2c4eb61 100644 --- a/src/components/NavBarStyles.css +++ b/src/components/NavBarStyles.css @@ -1,3 +1,95 @@ +@keyframes logo-dance-run { + 0% { + transform: translateX(-50%) scale(1) rotate(-5deg); + } + 5% { + transform: translateX(-50%) scale(1.08) rotate(8deg); + } + 10% { + transform: translateX(-50%) scale(1.12) rotate(-8deg); + } + 15% { + transform: translateX(-50%) scale(1.08) rotate(8deg); + } + 20% { + transform: translateX(-50%) scale(1) rotate(-5deg); + } + 40% { + transform: translateX(-50%) scale(1) rotate(0deg); + } + 60% { + transform: translateX(120vw) scale(1) rotate(0deg); + opacity: 0; + } + 61% { + transform: translateX(-120vw) scale(1) rotate(0deg); + opacity: 0; + } + 62% { + transform: translateX(-50%) scale(1) rotate(-5deg); + opacity: 1; + } + 100% { + transform: translateX(-50%) scale(1) rotate(-5deg); + opacity: 1; + } +} +.navbar-logo { + width: 55px; + height: 55px; + transition: width 0.3s, height 0.3s, margin 0.3s; + position: relative; + left: 0; + top: 0; + transform: none; + margin: 0 0 0 0; + z-index: 200; + display: block; +} + +@media (min-width: 601px) { + .navbar { + justify-content: flex-start; + } + .nav-brand { + width: auto; + display: flex; + justify-content: flex-start; + } + .navbar-logo { + width: 85px; /* 75px * 1.4 = 105px */ + height: 85px; + margin: 0 0 0 0; + top: 0; + left: 0; + display: block; + } +} + +@media (max-width: 600px) { + .navbar { + justify-content: flex-start; + } + .nav-brand { + width: auto; + display: flex; + justify-content: flex-start; + } + .navbar-logo { + width: 75px; + height: 75px; + margin: 0 0 0 0; + top: 0; + left: 0; + display: block; + } +} + +/* + === NAVBAR BACKGROUND COLOR === + To change the navbar color, edit the background property below. + Example: background: #e3f2fd; (baby blue) +*/ .navbar { display: flex; justify-content: space-between; @@ -5,16 +97,185 @@ position: sticky; top: 0; z-index: 100; - background-color: #f8f9fa; - padding: 1rem 2rem; - border-bottom: 1px solid #e9ecef; + background: #f5fdff; /* <-- NAVBAR COLOR (baby blue) */ + padding: 1rem 2rem 0.5rem 2rem; + border: none; + pointer-events: none; + min-height: 70px; + box-shadow: 0 2px 8px rgba(0,0,0,0.04); + margin-bottom: 2px; +} + +/* + === USERNAME PLACEMENT (Mobile) === + To move the username left, right, or center on mobile, edit the justify-content property below. + Example: justify-content: center; (centered), flex-start (left), flex-end (right) +*/ +/* + === USERNAME CONTAINER POSITIONING === + To move the username container up/down or adjust spacing, edit top, transform, and gap below. +*/ +.nav-user-info { + display: flex; + align-items: center; + gap: 0.5rem; + background: transparent; + pointer-events: auto; + justify-content: center; /* <-- USERNAME PLACEMENT (Mobile) */ + width: 100%; + position: absolute; + left: 0; + top: 46%; /* <-- Move up/down (was 50%) */ + transform: translateY(-34%); /* <-- Move up/down (was -50%) */ + z-index: 201; +} + +/* + === USERNAME STACK (Welcome + Username) === + To adjust vertical spacing between 'Welcome:' and username, edit gap and margin below. +*/ +/* + === USERNAME STACK HORIZONTAL POSITION (Mobile) === + To move the welcome/username stack right or left on mobile, edit margin-left below. + Example: margin-left: 12px; (moves right), 0 (default) +*/ +.username-stack { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.1rem; /* <-- Space between Welcome and username */ + margin-left: 26px; /* <-- Move welcome/username 12px to the right (Mobile) */ +} + +.username-welcome { + font-size: 0.95rem; + color: #888; + font-weight: 400; + margin-bottom: 0.1rem; /* <-- Space below Welcome */ + letter-spacing: 0.01em; +} +@media (min-width: 601px) { + /* + === USERNAME PLACEMENT (Desktop) === + To center the username container horizontally in the navbar on desktop, use left/top/transform below. + To move up/down, edit top/transform. To move left/right, edit left. + */ + .nav-user-info { + position: absolute; + left: 50%; /* <-- USERNAME CENTER (Desktop) */ + top: 46%; /* <-- Move up/down (was 50%) */ + transform: translate(-50%, -46%); + width: max-content; + margin-left: 0; + } +} + +/* + === HAMBURGER ICON POSITION === + To move the hamburger icon further right, edit the right property below. + Example: right: 2rem; (default), right: 2rem + 12px = calc(2rem - -12px) +*/ +.hamburger-container { + position: absolute; + right: calc(2rem - 28px); /* <-- Move hamburger 12px more to the right */ + top: 50%; + transform: translateY(-50%); + pointer-events: auto; +} + +/* Hamburger Menu */ +/* + The hamburger button is now fully clickable. To adjust the clickable area, edit width/height below. +*/ +/* + === HAMBURGER BUTTON CLICKABLE AREA === + To make the hamburger fully clickable, ensure width/height cover the whole icon and remove any gaps. + You can adjust width/height below for a larger or smaller clickable area. +*/ +.McButton { + width: 60px; /* <-- Clickable area width */ + height: 60px; /* <-- Clickable area height */ + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: none; + border: none; + cursor: pointer; + z-index: 300; + padding: 0; + gap: 0; + touch-action: manipulation; +} +/* + To change the color of the 3 lines (hamburger icon), edit the 'background' property below: + Example: background: #000; for black lines, background: #fff; for white lines +*/ +.McButton b { + display: block; + width: 44px; + height: 3px; + background: black; /* <-- HAMBURGER LINES COLOR */ + border-radius: 2px; + transition: all 0.3s cubic-bezier(.4,2.3,.3,1); + position: relative; + margin: 6px 0; /* Ensures lines are spaced but button is fully clickable */ +} +.McButton b:nth-child(1) { + top: 0; +} +.McButton b:nth-child(2) { + top: 0; +} +.McButton b:nth-child(3) { + top: 0; +} +.McButton.active b:nth-child(1) { + transform: translateY(15px) rotate(45deg); +} +.McButton.active b:nth-child(2) { + opacity: 0; +} +.McButton.active b:nth-child(3) { + transform: translateY(-15px) rotate(-45deg); +} + +/* + Hamburger dropdown uses absolute positioning and a very high z-index so it appears above the map but within the navbar area. + Adjust z-index if needed. +*/ +/* + Hamburger dropdown in portal: fixed to top right, always above all content. +*/ +/* + Hamburger dropdown in portal: fixed to top right, always above all content. + To adjust the left padding (space between button and left edge), change padding-left below. +*/ +.hamburger-dropdown-portal { + position: fixed; + top: 80px; + right: 2rem; + background: #fff; + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0,0,0,0.12); + padding: 8px 8px 8px 6px; /* <-- left padding reduced by 10px (was 16px) */ + min-width: 120px; + z-index: 99999; + display: flex; + flex-direction: column; + align-items: flex-end; + height: 48px; /* <-- Set dropdown height to match button height */ } + .nav-brand a { font-size: 1.5rem; font-weight: bold; color: #333; text-decoration: none; + background: transparent; + pointer-events: auto; } .nav-brand a:hover { @@ -24,12 +285,15 @@ .nav-links { display: flex; align-items: center; + background: transparent; + pointer-events: auto; } .auth-links { display: flex; gap: 1rem; align-items: center; + background: transparent; } .nav-link { @@ -38,35 +302,73 @@ padding: 0.5rem 1rem; border-radius: 4px; transition: all 0.2s; + background: rgba(0,0,0,0.2); + font-weight: bold; + box-shadow: 0 2px 8px rgba(0,0,0,0.08); } .nav-link:hover { color: #007bff; - background-color: #e9ecef; + background: rgba(0,0,0,0.4); } .user-section { display: flex; align-items: center; gap: 1rem; + background: transparent; } +/* + === USERNAME FONT SIZE (Mobile) === + To change the username font size on mobile, edit font-size below. +*/ .username { color: #666; font-weight: 500; + font-size: 1.33rem; /* <-- USERNAME FONT SIZE (Mobile) */ } +@media (min-width: 601px) { + /* + === USERNAME FONT SIZE (Desktop) === + To change the username font size on desktop, edit font-size below. + */ + .username { + font-size: 1.55rem; /* <-- USERNAME FONT SIZE (Desktop) */ + } +} + +/* + Logout button always fills the vertical height of the dropdown container. + On mobile, the button always shows the hover color. + To adjust the height, edit height below. To adjust color, edit background-color. +*/ + +/* + Logout button always shows #c82333 on mobile (no hover), and changes on hover for desktop browsers only. +*/ +/* + Logout button is solid #c82333 (no overlay) on all devices, with white text. + To adjust color, edit background-color below. +*/ .logout-btn { - background-color: #dc3545; - color: white; + background-color: #c82333; + color: #fff; border: none; - padding: 0.5rem 1rem; + padding: 6px 28px; border-radius: 4px; cursor: pointer; font-size: 0.875rem; transition: background-color 0.2s; + height: 100%; + box-sizing: border-box; + display: flex; + align-items: center; } -.logout-btn:hover { - background-color: #c82333; -} +@media (hover: hover) and (pointer: fine) { + .logout-btn:hover { + background-color: #c82333; + } +} \ No newline at end of file diff --git a/src/components/NowPlaying.jsx b/src/components/NowPlaying.jsx new file mode 100644 index 00000000..e104c96a --- /dev/null +++ b/src/components/NowPlaying.jsx @@ -0,0 +1,81 @@ +import React, { useEffect, useState } from "react"; +import axios from "../utils/axiosInstance"; +//import { API_URL } from "../shared"; + +const NowPlaying = ({ user }) => { + const [track, setTrack] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + if (!user) { + setTrack(null); + setError("You must be logged in to view the current track."); + return; + } + const fetchTrackFromBackend = async () => { + try { + // console.log( + // "Fetching current track from:", + // `${API_URL.trim()}/spotify/current-track` + // ); + const response = await axios.get("/api/spotify/current-track", + { + withCredentials: true, + } + ); + + const data = response.data; + + // Only set track if data is valid and has a title + if (data && data.title) { + setTrack(data); + setError(null); + } else { + setTrack(null); + } + } catch (err) { + console.error("Error fetching current track:", err); + setError("Unable to fetch currently playing track."); + } + }; + + fetchTrackFromBackend(); + const interval = setInterval(fetchTrackFromBackend, 10000); // Refresh every 10s + + return () => clearInterval(interval); + }, [user]); + + if (error) + return

{error}

; + if (!track) + return ( +

+ No song is currently playing. +

+ ); + + return ( +
+ {track.title} +

{track.title}

+

+ {track.artist} + {track.albumArt && track.album ? ( + <> + {" "} + — {track.album} + + ) : null} +

+
+ ); +}; + +export default NowPlaying; diff --git a/src/components/Signup.jsx b/src/components/Signup.jsx index 989fa096..fec76b10 100644 --- a/src/components/Signup.jsx +++ b/src/components/Signup.jsx @@ -147,7 +147,7 @@ const Signup = ({ setUser }) => {

- Already have an account? Login + Already have an account? Landing Page

diff --git a/src/shared.js b/src/shared.js index 818db4f2..d5a07dc4 100644 --- a/src/shared.js +++ b/src/shared.js @@ -1 +1,2 @@ -export const API_URL = process.env.API_URL || "http://localhost:8080"; +export const API_URL = "http://localhost:8080"; +export const IS_PRODUCTION = process.env.NODE_ENV === "production"; diff --git a/src/utils/axiosInstance.js b/src/utils/axiosInstance.js new file mode 100644 index 00000000..f8b6684b --- /dev/null +++ b/src/utils/axiosInstance.js @@ -0,0 +1,9 @@ +import axios from "axios"; +//import { API_URL } from "../shared"; + +const axiosInstance = axios.create({ + baseURL: process.env.API_URL || "http://127.0.0.1:8080", + withCredentials: true, +}) + +export default axiosInstance; \ No newline at end of file diff --git a/vercel.json b/vercel.json index a684d55b..b6f10de3 100644 --- a/vercel.json +++ b/vercel.json @@ -1,11 +1,30 @@ { "version": 2, + "outputDirectory": "dist", "routes": [ - { "src": "/main.js", "dest": "/main.js" }, - { "src": "/favicon.ico", "dest": "/favicon.ico" }, - { "src": "/react-logo.svg", "dest": "/react-logo.svg" }, - { "src": "/spongebob-404.webp", "dest": "/spongebob-404.webp" }, - { "src": "/(.*)", "dest": "/index.html" } - ], - "outputDirectory": "dist" -} + { + "src": "/images/(.*)", + "dest": "/images/$1" + }, + { + "src": "/main.js", + "dest": "/main.js" + }, + { + "src": "/favicon.ico", + "dest": "/favicon.ico" + }, + { + "src": "/react-logo.svg", + "dest": "/react-logo.svg" + }, + { + "src": "/spongebob-404.webp", + "dest": "/spongebob-404.webp" + }, + { + "src": "/(.*)", + "dest": "/index.html" + } + ] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index bfa8e19e..d043e00f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,15 +8,16 @@ module.exports = { output: { path: path.resolve(__dirname, "dist"), filename: "main.js", - publicPath: "/", + publicPath: "/", // needed for React Router }, devtool: "source-map", plugins: [ new webpack.EnvironmentPlugin({ - API_URL: "http://localhost:8080", + API_URL: "http://127.0.0.1:8080", REACT_APP_AUTH0_DOMAIN: "", REACT_APP_AUTH0_CLIENT_ID: "", REACT_APP_AUTH0_AUDIENCE: "", + REACT_APP_GOOGLE_MAPS_API_KEY: "", }), ], module: { @@ -31,6 +32,13 @@ module.exports = { }, }, }, + { + test: /\.(png|jpe?g|gif|svg)$/i, + type: "asset/resource", + generator: { + filename: "images/[hash][ext][query]", + }, + }, { test: /\.css$/, use: ["style-loader", "css-loader"], @@ -47,5 +55,7 @@ module.exports = { compress: true, historyApiFallback: true, port: 3000, + host: "127.0.0.1", + open: true, // Auto-open browser }, };