diff --git a/.env b/.env new file mode 100644 index 00000000..0864757a --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_APP_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkZGNmMGFlODhlOTY3NWRmMjE2NzE3MDVmM2NmMjkwNCIsIm5iZiI6MTc0OTUwNTU2NS4wNTYsInN1YiI6IjY4NDc1NjFkY2YxMDYwNmU1YjFlYWFhOCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.sWQ7x0l5lNvEmG4q6DheipSeFpWjC_H3TA9OQDWqjcs \ No newline at end of file diff --git a/Flixster_README b/Flixster_README new file mode 100644 index 00000000..3e4f3fe2 --- /dev/null +++ b/Flixster_README @@ -0,0 +1,126 @@ +## Unit Assignment: Flixster + +Submitted by: **Jack McClure** + +Estimated time spent: **14** hours spent in total + +Deployed Application (**required**): [Flixster Deployed Site](https://flixster-s5ux.onrender.com/) + +### Application Features + +#### REQUIRED FEATURES + +- [X] **Display Movies** + - [X] Users can view a list of current movies from The Movie Database API in a grid view. + - [X] Movie tiles should be reasonably sized (at least 6 playlists on your laptop when full screen; large enough that the playlist components detailed in the next feature are legible). + - [X] For each movie displayed, users can see the movie's: + - [X] Title + - [X] Poster image + - [X] Vote average + - [X] Users can load more current movies by clicking a button which adds more movies to the grid without reloading the entire page. +- [X] **Search Functionality** + - [X] Users can use a search bar to search for movies by title. + - [ ] The search bar should include: + - [X] Text input field + - [X] Submit/Search button + - [X] Clear button + - [X] Movies with a title containing the search query in the text input field are displayed in a grid view when the user either: + - [X] Presses the Enter key + - [X] Clicks the Submit/Search button + - [X] Users can click the Clear button. When clicked: + - [X] Most recent search results are cleared from the text input field and the grid view and all current movies are displayed in a grid view +- [X] **Design Features** + - [X] Website implements all of the following accessibility features: + - [X] Semantic HTML + - [X] [Color contrast](https://webaim.org/resources/contrastchecker/) + - [X] Alt text for images + - [X] Website implements responsive web design. + - [X] Uses CSS Flexbox or CSS Grid + - [X] Movie tiles and images shrink/grow in response to window size + - [X] Users can click on a movie tile to view more details about a movie in a pop-up modal. + - [X] The pop-up window is centered in the screen and does not occupy the entire screen. + - [X] The pop-up window has a shadow to show that it is a pop-up and appears floating on the screen. + - [X] The backdrop of the pop-up appears darker or in a different shade than before. including: + - [X] The pop-up displays additional details about the moving including: + - [X] Runtime in minutes + - [X] Backdrop poster + - [X] Release date + - [X] Genres + - [X] An overview + - [X] Users can use a drop-down menu to sort movies. + - [X] Drop-down allows movies to be sorted by: + - [X] Title (alphabetic, A-Z) + - [X] Release date (chronologically, most recent to oldest) + - [X] Vote average (descending, highest to lowest) + - [X] When a sort option is clicked, movies display in a grid according to selected criterion. + - [X] Website displays: + - [X] Header section + - [X] Banner section + - [X] Search bar + - [X] Movie grid + - [X] Footer section + - [X] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: To ease the grading process, please use the [color contrast checker](https://webaim.org/resources/contrastchecker/) to demonstrate to the grading team that text and background colors on your website have appropriate contrast. The Contrast Ratio should be above 4.5:1 and should have a green box surrounding it. + - [X] **Deployment** + - [X] Website is deployed via Render. + - [X] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: For ease of grading, please use the deployed version of your website when creating your walkthrough. + +#### STRETCH FEATURES + + +- [X] **Embedded Movie Trailers** + - [X] Within the pop-up modal displaying a movie's details, the movie trailer is viewable. + - [X] When the trailer is clicked, users can play the movie trailer. +- [X] **Favorite Button** + - [X] For each movie displayed, users can favorite the movie. + - [X] There should be visual element (such as a heart icon) on each movie's tile to show whether or not the movie has been favorited. + - [X] If the movie is not favorited: + - [X] Clicking on the visual element should mark the movie as favorited + - [X] There should be visual feedback (such as the heart turning a different color) to show that the movie has been favorited by the user. + - [X] If the movie is already favorited: + - [X] Clicking on the visual element should mark the movie as *not* favorited. + - [X] There should be visual feedback (such as the heart turning a different color) to show that the movie has been unfavorited. +- [X] **Watched Checkbox** + - [X] For each movie displayed, users can mark the movie as watched. + - [X] There should be visual element (such as an eye icon) on each movie's tile to show whether or not the movie has been watched. + - [X] If the movie has not been watched: + - [X] Clicking on the visual element should mark the movie as watched + - [X] There should be visual feedback (such as the eye turning a different color) to show that the movie has been watched by the user. + - [X] If the movie is already watched: + - [X] Clicking on the visual element should mark the movie as *not* watched. + - [X] There should be visual feedback (such as the eye turning a different color) to show that the movie has not been watched. +- [X] **Sidebar** + - [X] The website includes a side navigation bar. + - [X] The sidebar has three pages: + - [X] Home + - [X] Favorites + - [X] Watched + - [X] The Home page displays all current movies in a grid view, the search bar, and the sort movies drop-down. + - [X] The Favorites page displays all favorited movies in a grid view. + - [X] The Watched page displays all watched movies in a grid view. + +### Walkthrough Video + +`https://www.loom.com/share/f0a95dc25e9a4c12b51135f2e0e097d4` + +### Reflection + +* Did the topics discussed in your labs prepare you to complete the assignment? Be specific, which features in your weekly assignment did you feel unprepared to complete? + +Yes, the useState and prop drilling labs definitely helped me feel prepared to complete the assignment, however, I would say that the useEffect lessons didn't prepare us enough to deal with race conditions in the project. I also felt unprepared to complete the API pulling and async functions because the labs didn't cover tricky situations with await/async and where we could use each kind of function. + +* If you had more time, what would you have done differently? Would you have added additional features? Changed the way your project responded to a particular event, etc. + +If I had more time, I would've added a search bar the could search in the favorites and watched, but that would be more difficult because there isn't an API for it. I also would've used TMDB to store my watched/favorites list so it could be persistant even after page reloads. + +* Reflect on your project demo, what went well? Were there things that maybe didn't go as planned? Did you notice something that your peer did that you would like to try next time? + +Didn't demo this week (I already demoed last week). Next week, I would like to try adding color gradients for my CSS styling and more reactive color changes. + +### Open-source libraries used + +- Add any links to open-source libraries used in your project. + +### Shout out + +Give a shout out to somebody from your cohort that especially helped you during your project. This can be a fellow peer, instructor, TA, mentor, etc. +Shoutout to Liam Bremm for providing helpful feedback on CSS styling. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 92a683d2..fea0bd08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@types/react": "^18.2.66", @@ -45,13 +46,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -226,19 +229,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -253,39 +258,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", - "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/types": "^7.27.3" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -324,14 +318,15 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -359,27 +354,28 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -389,13 +385,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -405,13 +402,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -421,13 +419,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -437,13 +436,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -453,13 +453,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -469,13 +470,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -485,13 +487,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -501,13 +504,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -517,13 +521,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -533,13 +538,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -549,13 +555,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -565,13 +572,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -581,13 +589,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -597,13 +606,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -613,13 +623,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -629,13 +640,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -645,13 +657,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -661,13 +674,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -677,13 +691,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -693,13 +708,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -709,13 +725,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -725,13 +742,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -928,195 +946,280 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", + "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", + "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", + "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", + "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", + "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", + "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", + "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", + "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", + "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", + "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", + "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", + "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", "cpu": [ - "ppc64le" + "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", + "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", + "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", + "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", + "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", + "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", + "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", + "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", + "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1164,10 +1267,11 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/prop-types": { "version": "15.7.12", @@ -1265,18 +1369,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1533,35 +1625,6 @@ } ] }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1575,10 +1638,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1879,11 +1943,12 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1891,29 +1956,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -1925,15 +1990,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -2334,6 +2390,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2506,15 +2563,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -3160,9 +3208,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -3170,6 +3218,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3402,10 +3451,11 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/possible-typed-array-names": { "version": "1.0.0", @@ -3417,9 +3467,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", + "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", "dev": true, "funding": [ { @@ -3435,10 +3485,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3516,6 +3567,15 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3622,12 +3682,13 @@ } }, "node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", + "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -3637,21 +3698,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.42.0", + "@rollup/rollup-android-arm64": "4.42.0", + "@rollup/rollup-darwin-arm64": "4.42.0", + "@rollup/rollup-darwin-x64": "4.42.0", + "@rollup/rollup-freebsd-arm64": "4.42.0", + "@rollup/rollup-freebsd-x64": "4.42.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", + "@rollup/rollup-linux-arm-musleabihf": "4.42.0", + "@rollup/rollup-linux-arm64-gnu": "4.42.0", + "@rollup/rollup-linux-arm64-musl": "4.42.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-musl": "4.42.0", + "@rollup/rollup-linux-s390x-gnu": "4.42.0", + "@rollup/rollup-linux-x64-gnu": "4.42.0", + "@rollup/rollup-linux-x64-musl": "4.42.0", + "@rollup/rollup-win32-arm64-msvc": "4.42.0", + "@rollup/rollup-win32-ia32-msvc": "4.42.0", + "@rollup/rollup-win32-x64-msvc": "4.42.0", "fsevents": "~2.3.2" } }, @@ -3802,10 +3868,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -3909,18 +3976,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3939,15 +3994,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4100,14 +4146,15 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -4126,6 +4173,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -4143,6 +4191,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/package.json b/package.json index eded5715..94e6025f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/src/App.css b/src/App.css index 0bf65669..b6a6e2de 100644 --- a/src/App.css +++ b/src/App.css @@ -1,9 +1,25 @@ +/* https://colorhunt.co/palette/070f2b1b1a55535c919290c3 */ + + +:root { + --lucida-font: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; + --steelgray: #1e1e2f; + --martinique: #2a2a4c; + --butterflybush: #505091; + --wildblueyonder: #8080bc; + --gallery: #f0f0f0; +} + .App { text-align: center; + display: flex; + flex-direction: row; + width: 100%; + background-color: var(--steelgray); + min-height: 100vh; } .App-header { - background-color: #282c34; display: flex; flex-direction: row; align-items: center; @@ -12,6 +28,64 @@ padding: 20px; } +header { + display: flex; + flex-direction: row; + height: 200px; + width: 100%; + align-items: center; + justify-content: center; +} + +.header-text { + display:flex; + flex-direction: column; + align-items: center; + width: 100%; + color: var(--gallery); +} + +.flixster-title { + font-size: 60px; + margin-bottom: 20px; +} + +.banner { + font-size: 30px; + border: 2px var(--gallery) solid; + width: 80%; +} + +.header-image { + position: absolute; + top: 10px; + left: 10px; + object-fit: contain; + height: 200px; + width: 25%; +} + +.content { + display: flex; + flex-direction: column; + width: 80%; +} + +main { + display: flex; + flex-direction: row; + width: 100%; +} + +footer { + width: 100%; + background-color: #1B1A55; + position: relative; + bottom: 0; + color: var(--gallery); + margin-top: auto; +} + @media (max-width: 600px) { .movie-card { width: 100%; diff --git a/src/App.jsx b/src/App.jsx index dfa91584..285ea787 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,37 @@ import { useState } from 'react' import './App.css' +import SideBar from './components/SideBar/SideBar' +import MovieBox from './components/MovieBox/MovieBox' const App = () => { + const [mode, setMode] = useState("now-playing") + const [showSidebar, setShowSidebar] = useState(true) + + // Sidebar links/mode change handler + const handleModeChange = (newMode) => { + setMode(newMode) + } + + return (
- + + { showSidebar ? : null} +
+
+
+

Flixster

+ {/* Says banner component unrecognized in this browser */} +
Welcome to Flixster!
+
+
+
+ +
+ +
) } diff --git a/src/components/LoadMoreBar/LoadMoreBar.css b/src/components/LoadMoreBar/LoadMoreBar.css new file mode 100644 index 00000000..b64ce823 --- /dev/null +++ b/src/components/LoadMoreBar/LoadMoreBar.css @@ -0,0 +1,21 @@ +.loadmore-bar { + width: 100%; + height: 80px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.loadmore-button { + height: 40px; + width: 160px; + color: var(--gallery); + background-color: var(--butterflybush); + border-radius: 9px; +} + +.loadmore-button:hover { + background-color: var(--gallery); + color: var(--butterflybush); +} \ No newline at end of file diff --git a/src/components/LoadMoreBar/LoadMoreBar.jsx b/src/components/LoadMoreBar/LoadMoreBar.jsx new file mode 100644 index 00000000..4ab9c135 --- /dev/null +++ b/src/components/LoadMoreBar/LoadMoreBar.jsx @@ -0,0 +1,11 @@ +import './LoadMoreBar.css' + +const LoadMoreBar = ( {loadMoreHandler} ) => { + return ( +
+ +
+ ) +} + +export default LoadMoreBar \ No newline at end of file diff --git a/src/components/MovieBox/MovieBox.css b/src/components/MovieBox/MovieBox.css new file mode 100644 index 00000000..40e52fa2 --- /dev/null +++ b/src/components/MovieBox/MovieBox.css @@ -0,0 +1,11 @@ +.moviebox { + display: flex; + flex-direction: column; + width: 100%; + min-height: 100%; +} + +.no-more-movies { + font-size: 30px; + color: var(--gallery) +} \ No newline at end of file diff --git a/src/components/MovieBox/MovieBox.jsx b/src/components/MovieBox/MovieBox.jsx new file mode 100644 index 00000000..a59101aa --- /dev/null +++ b/src/components/MovieBox/MovieBox.jsx @@ -0,0 +1,220 @@ +import SearchBar from '../SearchBar/SearchBar' +import MovieList from '../MovieList/MovieList' +import LoadMoreBar from '../LoadMoreBar/LoadMoreBar' +import './MovieBox.css' +import { useEffect, useState } from 'react' +import { movieEntriesTitleSort, movieEntriesReleaseDateSort, movieEntriesVoteAverageSort } from '../../utils/sortingUtils' +import { fetchMovieByID, fetchMoviesBySearch } from '../../utils/apiUtils' + +//review1-branch + +const MovieBox = (mode) => { + // Movies is a dict + // Decoupled movies and ids to allow O(1) movie retrieval + const [movies, setMovies] = useState({}) + const [originalOrder, setOriginalOrder] = useState([]) + const [order, setOrder] = useState([]) + const [page, setPage] = useState(1) + const [morePages, setMorePages] = useState(true) + const [searchQuery, setSearchQuery] = useState("") + const [sortMode, setSortMode] = useState("none") + const [favorites, setFavorites] = useState([324544, 541671]) + const [watched, setWatched] = useState([447273]) + const [pageCleared, setPageCleared] = useState(false) + const [searchText, setSearchText] = useState("") + + // Used for fetching and processing favorites and watched movies + // Takes in a list of movieIDs, fetches corresponding movies from TMDB + // Replaces the movies, order, and originalOrder states + const fetchAndProcessMoviesByIDList = async (movieIDList) => { + const processMoviesByID = (movieData) => { + let movieDict = {} + movieData.map((movie) => movieDict[movie.id] = movie) + setMovies(movieDict) + switch (mode.mode) { + case "favorites": + setOriginalOrder(favorites) + setOrder(favorites) + break + case "watched": + setOriginalOrder(watched) + setOrder(watched) + break + } + } + const moviePromises = movieIDList.map((movieID) => fetchMovieByID(movieID)) + let movieData = await Promise.all(moviePromises) + processMoviesByID(movieData) + } + + // Used for fetching and processing now-playing and searched movies + // Takes in the page (and searchQuery) states to fetch relevant movies from TMDB + // Replaces the movies, order, and originalOrder states + const fetchAndProcessMoviesBySearch = async () => { + const processMoviesBySearch = (moviesData) => { + if (moviesData.total_pages === page) {setMorePages(false)} + let newMovies = {} + let newOrder = [] + // TMDB seems to be giving 1-2 repeats from the end of the last page on load more + moviesData.results.map( (movie) => { + if (!order.includes(movie.id) && !newOrder.includes(movie.id)) { + newMovies[movie.id] = movie + newOrder.push(movie.id) + } + }) + setOriginalOrder([...originalOrder, ...newOrder]) + newMovies = {...movies, ...newMovies} + newOrder = [...order, ...newOrder] + setOrder(newOrder) + setMovies(newMovies) + } + + let moviesData = await fetchMoviesBySearch(page, searchQuery) + processMoviesBySearch(moviesData) + } + + // Sorts movies based on sort mode (either title, release date, or vote average + // Works on the movies state dict and the order state list + const sortMovieOrder = () => { + if (movies.length === 0) { + return; + } + // Split movies dict into keys (id) and values (movie) + let movieEntries = Object.entries(movies) + // Listened to feedback about using switch/case for repeated if/else if or where it's easier to read + switch (sortMode) { + case "title": + movieEntriesTitleSort(movieEntries) + break; + case "release": + movieEntriesReleaseDateSort(movieEntries) + break; + case "vote": + movieEntriesVoteAverageSort(movieEntries) + break; + case "none": + setOrder(originalOrder) + return; + default: + console.error("Sort mode not found") + break + } + const newOrder = movieEntries.map( (entry) => entry[0]) + setOrder(newOrder) + } + + // If mode changes to now-playing, resets relevant states to prevent the favorites/watched movies from lingering + useEffect(() => { + if (mode.mode === "now-playing") { + setOrder([]) + setOriginalOrder([]) + setMovies({}) + setSortMode("none") + setPage(1) + setSearchQuery("") + updateSearchText("") + setPageCleared(true) + } + setSortMode("none") + }, [mode]) + + // Uses the mode change as a trigger to grab the relevant movies for new mode + // If mode changed to now-playing, needs setPageCleared(true) to load now-playing movies + // to prevent race conditions mentioned above + useEffect(() => { + switch (mode.mode) { + case "now-playing": + if(order.length === 0 && pageCleared) { + fetchAndProcessMoviesBySearch() + setPageCleared(false) + } + break + case "favorites": + fetchAndProcessMoviesByIDList(favorites) + break + case "watched": + fetchAndProcessMoviesByIDList(watched) + break + } + }, [mode, pageCleared]) + + // When Load More is pressed or searchQuery is updated + useEffect(() => { + fetchAndProcessMoviesBySearch(); + }, [page, searchQuery]) + + // When sortMode is updated or movies is udpated + // Assures sort after sortMode change AND when Load More adds more movies + useEffect(() => { + sortMovieOrder() + }, [sortMode, movies]) + + // Increments page + // (note if fetchAndProcessMoviesBySearch() loads last page for searchQuery, Load More button dissapeers) + const loadMore = () => { + setPage( (oldPage) => { + return oldPage + 1; + }) + } + + // Handles new search term + // If search term changes, it clears old movies/orders and resets page + const updateSearchQuery = (term) => { + if (!(term === searchQuery)) { + setSortMode("none") + setOrder([]) + setOriginalOrder([]) + setMovies({}) + setPage(1) + setSearchQuery(term) + } + } + + // Sort mode dropdown handler + const updateSortMode = (mode) => { + setSortMode(mode) + } + + // Sort mode dropdown handler + const updateSearchText = (text) => { + setSearchText(text) + } + + // Clear button handler + const handleClear = () => { + updateSortMode("none") + updateSearchText("") + updateSearchQuery("") + } + + // Favorite button handler + const toggleFavorite = (movieID) => { + let newFavorites = [...favorites] + if (newFavorites.includes(movieID)) { + newFavorites = newFavorites.filter( (id) => {return id !== movieID}) + } else { + newFavorites.push(movieID) + } + setFavorites(newFavorites) + } + + // Watched button handler + const toggleWatched = (movieID) => { + let newWatched = [...watched] + if (newWatched.includes(movieID)) { + newWatched = newWatched.filter( (id) => {return id !== movieID}) + } else { + newWatched.push(movieID) + } + setWatched(newWatched) + } + return ( +
+ + + { mode.mode === "now-playing" && !(Object.values(movies).length == 0) && morePages ? :

End of results...

} +
+ ) +} + +export default MovieBox \ No newline at end of file diff --git a/src/components/MovieCard/MovieCard.css b/src/components/MovieCard/MovieCard.css new file mode 100644 index 00000000..4e5182e0 --- /dev/null +++ b/src/components/MovieCard/MovieCard.css @@ -0,0 +1,80 @@ +.moviecard { + display: flex; + flex-direction: column; + width: 12%; + flex-basis: 12%; + border: 2px var(--steelgray) solid; + border-radius: 9px; + background-color: var(--martinique); + color: var(--gallery); + font-family: --lucida-font; + transition: all 0.2s; + margin: 1px; + min-width: 90px; +} + +.moviecard:hover { + scale: 105%; +} + +.moviecard-image { + border-top-left-radius: 6px; + border-top-right-radius: 6px; +} + +.moviecard-modalclickable { + display: flex; + flex-direction: column; + /* height: 70%; */ + flex-grow: 1; + /* color: #9290C3; */ + /* gap: 5%; */ + /* justify-content: center; */ +} + +.moviecard-modalclickable > p { + margin: auto; + padding: 5px; +} + +.moviecard-title { + height: 30%; +} + +/* .moviecard-vote { + height: 5%; +} */ + +.moviecard-modalclickable:hover { + cursor: pointer; +} + +.moviecard-buttons { + position: relative; + bottom: 3px; + display: flex; + flex-direction: row; + gap: 30%; + justify-content: center; + margin-top: 5px; +} + +.moviecard-buttons > button { + background-color: var(--butterflybush); + width: 30%; + border-radius: 9px; + transition: 0.2s; + display: flex; + justify-content: center; + align-items: center; + margin-top: 10px; +} + +.moviecard-buttons > button:hover { + scale: 105%; + background-color: var(--wildblueyonder); +} + +.movielist { + background-color: var(--butterflybush); +} \ No newline at end of file diff --git a/src/components/MovieCard/MovieCard.jsx b/src/components/MovieCard/MovieCard.jsx new file mode 100644 index 00000000..6895b31e --- /dev/null +++ b/src/components/MovieCard/MovieCard.jsx @@ -0,0 +1,23 @@ +import './MovieCard.css' +import { useState } from 'react' +import MovieModal from '../MovieModal/MovieModal' +import { BsEyeSlash, BsEyeFill } from "react-icons/bs"; +import { FaStar, FaRegStar } from "react-icons/fa"; + +const MovieCard = ( {movie, favorites, watched, toggleFavorite, toggleWatched, openModal } ) => { + return ( +
+
openModal(movie.id)}> + {`${movie.title} +

{movie.title}

+

{movie.vote_average}

+
+
+ + +
+
+ ) +} + +export default MovieCard \ No newline at end of file diff --git a/src/components/MovieList/MovieList.css b/src/components/MovieList/MovieList.css new file mode 100644 index 00000000..69db7edb --- /dev/null +++ b/src/components/MovieList/MovieList.css @@ -0,0 +1,13 @@ +.movielist { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 30px; + margin: 30px; + justify-content: center; + padding-top: 30px; + border-radius: 9px; + background-color: var(--butterflybush); + padding-bottom: 30px; + margin-bottom: 0; +} \ No newline at end of file diff --git a/src/components/MovieList/MovieList.jsx b/src/components/MovieList/MovieList.jsx new file mode 100644 index 00000000..80e78ca3 --- /dev/null +++ b/src/components/MovieList/MovieList.jsx @@ -0,0 +1,33 @@ +import MovieCard from '../MovieCard/MovieCard' +import './MovieList.css' +import MovieModal from '../MovieModal/MovieModal' +import { useState } from 'react' + +const MovieList = ( {movies, order, favorites, watched, toggleFavorite, toggleWatched} ) => { + const [showModal, setShowModal] = useState(false) + const [modalMovieID, setModalMovieID] = useState(0) + + const closeModal = (e) => { + if (e.currentTarget === e.target) { + setShowModal(false) + } + } + const openModal = (id) => { + setModalMovieID(id) + setShowModal(true) + } + + return ( +
+ { + movies.length === 0 ?

No movies found!

: + movies && order && order.map( (id) => { + return + }) + } + { showModal ? : null} +
+ ) +} + +export default MovieList \ No newline at end of file diff --git a/src/components/MovieModal/MovieModal.css b/src/components/MovieModal/MovieModal.css new file mode 100644 index 00000000..7ca397f2 --- /dev/null +++ b/src/components/MovieModal/MovieModal.css @@ -0,0 +1,106 @@ +.moviemodal { + display: block; + position: fixed; + z-index: 2; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.4); +} + +.moviemodal-content { + background-color: var(--martinique); + margin: 100px auto; + border: 1px solid var(--martinique); + width: 75vw; + max-width: 100%; + height: 80%; + position: relative; + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: auto; + font-family: --lucida; + color: var(--gallery) +} + +.moviemodal-content > * { + width: 100%; +} + +.moviemodal-close-button { + position: absolute; + right: 0; + top: 0; + width: 30px; + border: none; + margin-top: 10px; + margin-right: 10px; + border-radius: 9px; + background-color: var(--wildblueyonder); + color: var(--gallery); + transition: 0.2s; +} + +.moviemodal-close-button:hover { + scale: 125%; +} + +.moviemodal-content-image { + object-fit: contain; + width: 60%; + height: 40%; + margin-top: 400px; +} + +.genre-header { + margin-bottom: 5px; +} + +.moviemodal-content-info-genres { + display: flex; + flex-direction: row; + gap: 15px; + justify-content: center; +} + +.moviemodal-content-videos { + display: flex; + flex-direction: column; + width: 60%; + height: 60%; +} + +iframe { + margin: 15px; + width: 100%; + height: 500px; +} + +.trailer-select { + display: flex; + flex-direction: row; + gap: 15px; + justify-content: center; + padding-bottom: 15px; +} + +.trailer-select > button { + border: none; + background-color: var(--steelgray); + transition: 0.2s; +} + +.trailer-select > button:hover { + border: none; + border-radius: 9px; + background-color: var(--wildblueyonder); +} + + +.moviemodal-content-info { + width: 80% +} \ No newline at end of file diff --git a/src/components/MovieModal/MovieModal.jsx b/src/components/MovieModal/MovieModal.jsx new file mode 100644 index 00000000..4c1b3157 --- /dev/null +++ b/src/components/MovieModal/MovieModal.jsx @@ -0,0 +1,66 @@ +import './MovieModal.css' +import { useState, useEffect } from 'react' +import { fetchMovieByID, fetchMovieVideosByID } from '../../utils/apiUtils' + +const MovieModal = ({movieID, closeModal}) => { + const [movie, setMovie] = useState({}) + const [trailers, setTrailers] = useState([]) + const [currentTrailer, setCurrentTrailer] = useState(0) + + const fetchAndProcessMovieDetails = async (movieID) => { + const movieDetails = await fetchMovieByID(movieID) + setMovie(movieDetails) + } + + const fetchAndProcessMovieTrailers = async (movieID) => { + const movieVideos = await fetchMovieVideosByID(movieID) + const trailers = movieVideos.results.filter( (video) => {return video.type === "Trailer"}) + setTrailers(trailers) + } + + useEffect( () => { + fetchAndProcessMovieDetails(movieID) + fetchAndProcessMovieTrailers(movieID) + }, []) + + return ( +
closeModal(e)}> +
+ + {`${movie.title} +
+

{movie.title}

+

{movie.overview}

+

Runtime: {movie.runtime} minutes

+

Release Date: {movie.release_date}

+

Genres:

+
+ {movie.genres && movie.genres.map( (genreEntry) => { + return ( +

{genreEntry.name}

+ ) + })} +
+
+ { trailers.length > 0 && trailers[currentTrailer] ? +
+ +
+ +

{trailers[currentTrailer].name} ({currentTrailer+1}/{trailers.length})

+ +
+
+ :

No trailers found!

} +
+
+ ) +} + +export default MovieModal \ No newline at end of file diff --git a/src/components/SearchBar/SearchBar.css b/src/components/SearchBar/SearchBar.css new file mode 100644 index 00000000..e98611f7 --- /dev/null +++ b/src/components/SearchBar/SearchBar.css @@ -0,0 +1,81 @@ +.searchbar { + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; +} + +.searchbar-searchbar { + display: flex; + flex-direction: row; + gap: 5%; + background-color: var(--martinique); + width: 40%; + justify-self: center; + justify-content: center; + align-items: center; + border-radius: 9px; + height: 40px; + min-width: 400px; +} + +input::placeholder { + color: black; + opacity: 0.8 +} + +.searchbar-input { + height: 20px; + width: 50%; + border-radius: 9px; + margin-left: 5px; + border: 3px solid var(--steelgray); +} + + +.searchbar-submit { + background-color: var(--butterflybush); + border-radius: 8px; + height: 30px; + padding-bottom: 3px; + border: 2px var(--gallery) solid; + transition: 0.2s; +} + +.searchbar-submit:hover { + scale: 105%; +} + +.searchbar-clear { + background-color: var(--butterflybush); + border-radius: 8px; + height: 30px; + padding-bottom: 3px; + border: 2px var(--gallery) solid; + transition: 0.2s; + margin-right: 8px; +} + +.searchbar-clear:hover { + scale: 105%; +} + + +.searchbar-sort { + display: flex; + flex-direction: row; + background-color: var(--martinique); + width: 20%; + justify-content: center; + border-radius: 9px; + height: 35px; + align-items: center; + min-width: 220px; +} + +.searchbar-sort-dropdown { + height: 25px; + width: 90%; + border: 3px solid var(--steelgray); + border-radius: 9px; +} \ No newline at end of file diff --git a/src/components/SearchBar/SearchBar.jsx b/src/components/SearchBar/SearchBar.jsx new file mode 100644 index 00000000..c1cabbe6 --- /dev/null +++ b/src/components/SearchBar/SearchBar.jsx @@ -0,0 +1,27 @@ +import './SearchBar.css' +import { useState } from 'react' + +const SearchBar = ({ mode, searchQuery, searchText, searchTextHandler, searchHandler, sortMode, sortHandler, clearHandler }) => { + return ( +
+ { mode === "now-playing" ? +
+ { if (e.key === "Enter") {searchHandler(searchText)}}} onChange={(e) => searchTextHandler(e.target.value)} value={searchText} placeholder="Search for movies..."> + + {/* { !(searchQuery === "") ? : null } */} + +
: null + } +
+ +
+
+ ) +} + +export default SearchBar \ No newline at end of file diff --git a/src/components/SideBar/SideBar.css b/src/components/SideBar/SideBar.css new file mode 100644 index 00000000..c37f04f8 --- /dev/null +++ b/src/components/SideBar/SideBar.css @@ -0,0 +1,30 @@ +.sidebar { + display: flex; + flex-direction: column; + gap: 40px; + width: 20%; + margin-top: 30px; + gap: 50px; +} + +.sidebar-image { + object-fit: contain; + width: 15vw; + height: 200px; + align-self: center; + margin-left: 30px; +} + +.sidebar-links { + display: flex; + flex-direction: column; + gap: 30px; + color: var(--gallery); + transition: 0.2s; +} + +.sidebar-links > a:hover { + cursor: pointer; + text-decoration: underline; + scale: 105%; +} \ No newline at end of file diff --git a/src/components/SideBar/SideBar.jsx b/src/components/SideBar/SideBar.jsx new file mode 100644 index 00000000..33afeca4 --- /dev/null +++ b/src/components/SideBar/SideBar.jsx @@ -0,0 +1,22 @@ +import './SideBar.css' + +const SideBarLinks = ({modeHandler}) => { + return ( +
+ modeHandler("now-playing")}>Home + modeHandler("favorites")}>Favorites + modeHandler("watched")}>Watched +
+ ) +} + +const SideBar = ({modeHandler}) => { + return ( +
+ flixster movie projector logo + +
+ ) +} + +export default SideBar \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 54b39dd1..fce89a24 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,7 +1,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' -import './index.css' +import './styles/index.css' ReactDOM.createRoot(document.getElementById('root')).render( diff --git a/src/movie-projector-logo.jpg b/src/movie-projector-logo.jpg new file mode 100644 index 00000000..b77454c3 Binary files /dev/null and b/src/movie-projector-logo.jpg differ diff --git a/src/index.css b/src/styles/index.css similarity index 79% rename from src/index.css rename to src/styles/index.css index e1faed1a..20255f5a 100644 --- a/src/index.css +++ b/src/styles/index.css @@ -11,9 +11,4 @@ button { font-size: 16px; font-weight: bold; transition: background-color 0.3s ease; -} - -button:hover { - background-color: #777; - color: white; -} +} \ No newline at end of file diff --git a/src/utils/apiUtils.jsx b/src/utils/apiUtils.jsx new file mode 100644 index 00000000..4b97c2dd --- /dev/null +++ b/src/utils/apiUtils.jsx @@ -0,0 +1,66 @@ +export const fetchMovieByID = async (movieID) => { + try { + const apiKey = import.meta.env.VITE_APP_API_KEY + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + let response = null + response = await fetch(`https://api.themoviedb.org/3/movie/${movieID}?language=en-US`, options) + if (!response.ok) {throw new Error('Failed to fetch movie by ID')} + const data = await response.json() + return data + } catch (error) { + console.error(error) + } +} + +export const fetchMoviesBySearch = async (page, searchQuery) => { + const buildMovieSearchURL = (page, searchQuery) => { + return searchQuery === "" + ? `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}&include_adult=false` + :`https://api.themoviedb.org/3/search/movie?query=${searchQuery}&include_adult=false&language=en-US&page=${page}` + } + try { + const apiKey = import.meta.env.VITE_APP_API_KEY + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + const searchURL = buildMovieSearchURL(page, searchQuery) + let response = await fetch(searchURL, options) + if (!response.ok) {throw new Error('Failed to fetch movies by search')} + const data = await response.json() + return data + } catch (error) { + console.error(error) + } +} + +export const fetchMovieVideosByID = async (movieID) => { + try { + const apiKey = import.meta.env.VITE_APP_API_KEY + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + let response = null + response = await fetch(`https://api.themoviedb.org/3/movie/${movieID}/videos?language=en-US`, options) + if (!response.ok) { + throw new Error('Failed to fetch videos') + } + const data = await response.json() + return data + } catch (error) { + console.error(error) + } +} \ No newline at end of file diff --git a/src/utils/sortingUtils.js b/src/utils/sortingUtils.js new file mode 100644 index 00000000..9072eaa6 --- /dev/null +++ b/src/utils/sortingUtils.js @@ -0,0 +1,29 @@ +// Takes a list of movie entries (a list of {id: int, movie: {}}) and sorts alphabetically by title property +export const movieEntriesTitleSort = (movieEntries) => { + movieEntries.sort((left_entry, right_entry) => { + if (left_entry[1].title < right_entry[1].title) {return -1;} + if (left_entry[1].title > right_entry[1].title) {return 1;} + return 0; + }) + return movieEntries +} + +// Takes a list of movie entries (a list of {id: int, movie: {}}) and sorts by release date property (newest to oldest) +export const movieEntriesReleaseDateSort = (movieEntries) => { + // Listened to feedback about shortening sorting comparator + // Note: can't subtract strings in JS + movieEntries.sort((left_entry, right_entry) => { + const left_date = new Date(left_entry[1].release_date) + const right_date = new Date(right_entry[1].release_date) + return right_date - left_date + }) + return movieEntries +} + +// Takes a list of movie entries (a list of {id: int, movie: {}}) and sorts by vote average property (highest to lowest) +export const movieEntriesVoteAverageSort = (movieEntries) => { + movieEntries.sort((left_entry, right_entry) => { + return right_entry[1].vote_average-left_entry[1].vote_average + }) + return movieEntries +} \ No newline at end of file