diff --git a/README.md b/README.md
index c0541f9..ce89c09 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,7 @@
-# Getting Started with Create React App
+# Prittee
+
+Prittee is an unofficial, experimental alternate front end for Pithee, the site Jacksfilms recent launched. I made Prittee because I noticed some small UI/UX issues I thought could be fixed to improve the user experience of the site (most notably, the time it takes to load more posts after voting for one).
-This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
+Prittee has some limitations due to it being an unofficial site, such as the fact that you can only be logged in for an hour at a time, so I don't expect this to replace the official Pithee site for people's pith-ing needs. This was mostly made as an exercise for me.
-## Available Scripts
-
-In the project directory, you can run:
-
-### `yarn start`
-
-Runs the app in the development mode.\
-Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
-
-The page will reload when you make changes.\
-You may also see any lint errors in the console.
-
-### `yarn test`
-
-Launches the test runner in the interactive watch mode.\
-See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
-
-### `yarn build`
-
-Builds the app for production to the `build` folder.\
-It correctly bundles React in production mode and optimizes the build for the best performance.
-
-The build is minified and the filenames include the hashes.\
-Your app is ready to be deployed!
-
-See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
-
-### `yarn eject`
-
-**Note: this is a one-way operation. Once you `eject`, you can't go back!**
-
-If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
-
-Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
-
-You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
-
-## Learn More
-
-You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
-
-To learn React, check out the [React documentation](https://reactjs.org/).
-
-### Code Splitting
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
-
-### Analyzing the Bundle Size
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
-
-### Making a Progressive Web App
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
-
-### Advanced Configuration
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
-
-### Deployment
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
-
-### `yarn build` fails to minify
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
+The site is made in React, using React Router v6.
\ No newline at end of file
diff --git a/image-1.png b/image-1.png
new file mode 100644
index 0000000..fa3bfd8
Binary files /dev/null and b/image-1.png differ
diff --git a/image-2.png b/image-2.png
new file mode 100644
index 0000000..80c2c39
Binary files /dev/null and b/image-2.png differ
diff --git a/image.png b/image.png
new file mode 100644
index 0000000..eaca7ac
Binary files /dev/null and b/image.png differ
diff --git a/package.json b/package.json
index 21334cf..224e890 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,17 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@emotion/react": "^11.11.4",
+ "@emotion/styled": "^11.11.5",
+ "@mui/icons-material": "^5.15.14",
+ "@mui/lab": "^5.0.0-alpha.169",
+ "@mui/material": "^5.15.14",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.0"
},
@@ -34,5 +40,10 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "autoprefixer": "^10.4.19",
+ "postcss": "^8.4.38",
+ "tailwindcss": "^3.4.3"
}
}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..33ad091
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/public/favicon.ico b/public/favicon.ico
index a11777c..3ff6943 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
index aa069f2..46ebb19 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,11 +3,11 @@
-
+
- React App
+ Prittee - A Pithee Client
You need to enable JavaScript to run this app.
diff --git a/public/logo192.png b/public/logo192.png
index fc44b0a..04aca44 100644
Binary files a/public/logo192.png and b/public/logo192.png differ
diff --git a/public/manifest.json b/public/manifest.json
index 080d6c7..1ce45ac 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,6 +1,6 @@
{
- "short_name": "React App",
- "name": "Create React App Sample",
+ "short_name": "Prittee",
+ "name": "An unofficial front end for Pithee",
"icons": [
{
"src": "favicon.ico",
diff --git a/src/App.css b/src/App.css
index 74b5e05..1914e7d 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,38 +1,21 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
+#winnerPostAuthor {
+ font-size: 14px;
}
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
+.prevent-select {
+ -webkit-user-select: none; /* Safari */
+ -ms-user-select: none; /* IE 10 and IE 11 */
+ user-select: none; /* Standard syntax */
}
-.App-link {
- color: #61dafb;
+/* Hide scrollbar for Chrome, Safari and Opera */
+.no-scrollbar::-webkit-scrollbar {
+ display: none;
}
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
+/* Hide scrollbar for IE, Edge and Firefox */
+.no-scrollbar {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+}
\ No newline at end of file
diff --git a/src/App.js b/src/App.js
index 3784575..cba860e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,24 +1,29 @@
-import logo from './logo.svg';
import './App.css';
+import { Box, Container } from '@mui/material';
+import { Outlet } from 'react-router-dom';
+import Header from './Components/Header';
+import Footer from './Components/Footer';
+
+// TODO:
+// - Leaderboard past #25 (pagination)
function App() {
return (
-
+
+
+
+
+
+
+
);
}
diff --git a/src/Components/Countdown.jsx b/src/Components/Countdown.jsx
new file mode 100644
index 0000000..29ece95
--- /dev/null
+++ b/src/Components/Countdown.jsx
@@ -0,0 +1,38 @@
+import { Box } from "@mui/material";
+import { useState, useEffect } from "react";
+
+export default function Countdown({ until, onEnd }) {
+
+ const t1 = new Date()
+ const t2 = new Date(until) // 10 minutes after win time
+ const msUntil = (t2.getTime()+10*60000) - t1.getTime();
+ const secondsUntil = Math.floor(msUntil / 1000)
+ const [seconds, setSeconds] = useState(secondsUntil)
+ useEffect(() => {
+ var interval = setInterval(() => {
+ setSeconds(s => s-1)
+ if (seconds < 1) {
+ clearInterval(interval)
+ onEnd()
+ }
+ }, 1000);
+ return function cleanup() {
+ clearInterval(interval);
+ };
+ }, [seconds])
+
+ const formatTime = (time) => {
+ if (time <= 0) {
+ return '0:00'
+ }
+ let m = Math.floor(time / 60)
+ let s = time - (m * 60)
+ let lead = s < 10 ? '0' : ''
+ return `${m}:${lead+s}`
+ }
+ return (
+
+ {formatTime(seconds)}
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/Footer.jsx b/src/Components/Footer.jsx
new file mode 100644
index 0000000..059c3f8
--- /dev/null
+++ b/src/Components/Footer.jsx
@@ -0,0 +1,10 @@
+import { Container, Box } from "@mui/material";
+
+export default function Footer() {
+ return (
+
+ View the source on GitHub
+ Report a bug
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/Header.jsx b/src/Components/Header.jsx
new file mode 100644
index 0000000..ec2e0d0
--- /dev/null
+++ b/src/Components/Header.jsx
@@ -0,0 +1,29 @@
+import { Box, Container } from "@mui/material"
+import { NavLink } from "react-router-dom";
+import { AccountBoxOutlined, PostAdd, LeaderboardOutlined } from '@mui/icons-material';
+import { useNavigate } from "react-router-dom";
+
+export default function Header() {
+
+ const navigate = useNavigate()
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/Leaderboard.jsx b/src/Components/Leaderboard.jsx
new file mode 100644
index 0000000..6247369
--- /dev/null
+++ b/src/Components/Leaderboard.jsx
@@ -0,0 +1,21 @@
+import { Box } from "@mui/material";
+
+export default function Leaderboard({ users }) {
+ let i = 0
+ return (
+
+ {users.map((user) => {
+ i++
+ return (
+
+
+ {i}.
+
+
+ {user.player_name}
+ {user.total_points}
+
+ )})}
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/LeaderboardSkeleton.jsx b/src/Components/LeaderboardSkeleton.jsx
new file mode 100644
index 0000000..9f95abc
--- /dev/null
+++ b/src/Components/LeaderboardSkeleton.jsx
@@ -0,0 +1,9 @@
+import { LinearProgress, Box } from "@mui/material";
+
+export default function LeaderboardSkeleton() {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/Post.jsx b/src/Components/Post.jsx
new file mode 100644
index 0000000..24f92c7
--- /dev/null
+++ b/src/Components/Post.jsx
@@ -0,0 +1,58 @@
+import { Box } from "@mui/material";
+import { LinearProgress } from '@mui/material'
+import { Key, Star } from "@mui/icons-material";
+
+export default function Post({ text, tier, index, id, onClick }) {
+ const renderStars = () => {
+ const arr = []
+ for (let i = 0; i < tier; i++) {
+ arr.push(
+
+ )
+ }
+ return arr
+ }
+ if (text === '') {
+ return (
+
+
+
+ )
+ } else {
+ return (
+ onClick({post_text: text, index: index, tier: tier, id: id})}>
+
+ {renderStars()}
+
+ {text}
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/Components/PostGrid.jsx b/src/Components/PostGrid.jsx
new file mode 100644
index 0000000..dbad3b6
--- /dev/null
+++ b/src/Components/PostGrid.jsx
@@ -0,0 +1,164 @@
+import { Grid } from '@mui/material';
+import Post from './Post';
+import { declareVictor, fetchPosts, pushPostScore } from '../utils'
+import { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import Queue from '../queue'
+import { getFullContestants, lenContestants, rmContestant } from '../contestants';
+
+const postsSkeleton = [
+ {
+ post_text: '',
+ index: 0,
+ id: '1',
+ tier: 0,
+ },
+ {
+ post_text: '',
+ index: 1,
+ id: '2',
+ tier: 0,
+ },
+ {
+ post_text: '',
+ index: 2,
+ id: '3',
+ tier: 0,
+ },
+ {
+ post_text: '',
+ index: 3,
+ id: '4',
+ tier: 0,
+ },
+]
+
+const PostQueue = new Queue()
+
+export default function PostGrid() {
+ const navigate = useNavigate()
+ const [selected, setSelected] = useState(null)
+ const [posts, setPosts] = useState(postsSkeleton)
+ const [loading, setLoading] = useState(false)
+
+ // every time data state changes i.e. every time a user clicks on post,
+ // update posts with their selection
+ // note: this also will load the initial posts
+ useEffect(()=> {
+ //
+ // rebuild post array with kept post
+ // render new array
+ updatePosts(selected)
+ }, [selected])
+
+ // handler passed to Post children to let us know when one has been selected
+ const selectPost = (post) => {
+ post.tier += 1
+ if (post.tier == 4) { // triggers on a showdown winner
+ declareVictor(post)
+ setSelected(null)
+ // return early because we already handled selecting a showdown winner
+ return
+ }
+ // one of these if statements will trigger when a
+ // post has reached the end of its ranking,
+ // or if it has reached max-tier respectively
+ if (selected != null && post.id != selected.id && selected.tier != 3) {
+ pushPostScore(selected)
+ }
+ if (post.tier == 3){
+ pushPostScore(post)
+ }
+ // triggers when it's time for a showdown
+ if (lenContestants() >= 4) {
+ setPosts(getFullContestants())
+ // return early so as to not trigger the updatePosts logic and
+ // instead render the showdown
+ return
+ }
+ setSelected(post)
+ }
+
+ // handles updating the post state
+ const refreshPosts = (keep) => {
+ // this line prevents trying to refresh posts on mount, when
+ // no posts have actually been loaded yet
+ if (PostQueue.size() < 4) { return }
+ const arr = []
+ for (var i = 0; i < 4; i++) {
+ if (keep != null && keep.index == i && keep.tier < 3) {
+ arr.push(keep)
+ } else {
+ let p = PostQueue.pop()
+ arr.push(
+ {
+ post_text: p.post_text,
+ index: i,
+ id: p.post_id,
+ tier: 0,
+ }
+ )
+ }
+ }
+ setPosts(arr)
+ }
+
+ // handles refilling the queue when needed, then calls refreshPosts
+ const updatePosts = (keep) => {
+ console.log(`loading: ${loading}`)
+ // if the queue is really low, we want to stop the user from making any more
+ // selections to let the queue reload
+ if (PostQueue.size() <= 4) {
+ if (loading) {
+ console.log('user is clicking too fast!')
+ // if we are already loading in new posts and the user
+ // is basically spam clicking, just return right away to
+ // prevent it from requesting more posts
+ return
+ }
+ setLoading(true)
+ console.log('loading last-second posts...')
+ fetchPosts().then((r) => {
+ if (r == null) {
+ } else {
+ r.forEach(npost => {
+ PostQueue.push(npost)
+ });
+ refreshPosts(keep)
+ setLoading(false)
+ }
+ })
+ } else if (PostQueue.size() <= 11 && !loading) {
+ // somewhat low queue -> fetch more posts in the background
+ setLoading(true)
+ console.log('loading more posts...')
+ fetchPosts().then((r) => {
+ if (r == null) {
+ alert("Your token has expired. Please re-enter your information to keep using Prittee.")
+ navigate('/login')
+ } else {
+ r.forEach(npost => {
+ PostQueue.push(npost)
+ });
+ setLoading(false)
+ }
+ })
+ refreshPosts(keep)
+ } else {
+ // full queue -> keep going as normal
+ refreshPosts(keep)
+ }
+ }
+
+ return (
+
+ {posts.map((p) => {
+ return(
+
+
+
+ )})}
+
+ )
+}
+
diff --git a/src/Components/PreviousWinner.jsx b/src/Components/PreviousWinner.jsx
new file mode 100644
index 0000000..7709fcd
--- /dev/null
+++ b/src/Components/PreviousWinner.jsx
@@ -0,0 +1,12 @@
+import { Box } from "@mui/material";
+
+export default function PreviousWinner({ post }) {
+ let dateString = new Date(post.won_at).toLocaleString()
+ return (
+
+ {post.post_text}
+ by {post.player_name}
+ Won: {dateString}
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/UserPost.jsx b/src/Components/UserPost.jsx
new file mode 100644
index 0000000..6eda737
--- /dev/null
+++ b/src/Components/UserPost.jsx
@@ -0,0 +1,14 @@
+import { Box } from "@mui/material";
+
+export default function UserPost({ post }) {
+ let dateString = new Date(post.created_at).toLocaleString()
+ return (
+
+ {post.post_text}
+ Points: {post.points}
+ Posted: {dateString}
+ {/* I actually don't know if won_at is the right key because i haven't won yet LOL */}
+ {post.won_at ? Posted: {dateString} : <>>}
+
+ )
+}
\ No newline at end of file
diff --git a/src/Components/WinArchive.jsx b/src/Components/WinArchive.jsx
new file mode 100644
index 0000000..3eb433d
--- /dev/null
+++ b/src/Components/WinArchive.jsx
@@ -0,0 +1,15 @@
+import { Box } from "@mui/material";
+import PreviousWinner from "./PreviousWinner";
+
+export default function WinArchive({ data }) {
+ return(
+
+ {data.map((post) => )}
+
+ )
+}
\ No newline at end of file
diff --git a/src/Pages/CreatePost.jsx b/src/Pages/CreatePost.jsx
new file mode 100644
index 0000000..120da40
--- /dev/null
+++ b/src/Pages/CreatePost.jsx
@@ -0,0 +1,33 @@
+import { TextField } from '@mui/material'
+import Box from '@mui/material/Box'
+import LoadingButton from '@mui/lab/LoadingButton'
+import { useState } from 'react'
+import { insertPost } from '../utils'
+import { useNavigate } from 'react-router-dom'
+
+export default function CreatePost() {
+ const [text, setText] = useState('')
+ const [loading, setLoading] = useState(false)
+ const navigate = useNavigate()
+
+ const submitHandler = () => {
+ if (text.length > 80) {
+ return
+ }
+ setLoading(true)
+ insertPost(text)
+ .then(() => {
+ navigate('/')
+ setLoading(false)
+ })
+ }
+ return (
+
+ 80 ? 'error' : 'primary'} label="Make a post" multiline rows={3} margin='normal' onChange={(e) => setText(e.target.value)}/>
+ 80 ? 'text-red-400' : 'text-neutral-300'}>{text.length}/80
+ Submit
+
+ )
+}
+
+//i saw a bank that offered "24 hour service" and I thought "it takes that long??"
\ No newline at end of file
diff --git a/src/Pages/Index.jsx b/src/Pages/Index.jsx
new file mode 100644
index 0000000..f1fff9f
--- /dev/null
+++ b/src/Pages/Index.jsx
@@ -0,0 +1,52 @@
+import { Box } from '@mui/material';
+import PostGrid from '../Components/PostGrid'
+import { useLoaderData } from 'react-router-dom';
+import Countdown from '../Components/Countdown';
+import { useState } from 'react';
+import { loadCurrentWinner } from '../utils';
+import WinArchive from '../Components/WinArchive';
+
+export default function Index({ cw, wa }) {
+
+ const data = useLoaderData()
+ const [winArchive, setWinArchive] = useState(data[0])
+ const [currentWinner, setCurrentWinner] = useState(data[1][0]) // why is the current winner api response an array??
+ const [winnerTime, setWinnerTime] = useState(new Date(currentWinner.won_at)) // why is the current winner api response an array??
+ const [showWinArchive, setShowWinArchive] = useState(false)
+
+ const reloadWinner = async () => {
+ console.log('loading new winner')
+ loadCurrentWinner().then(r => {
+ setCurrentWinner(r[0])
+ setWinnerTime(new Date(currentWinner.won_at))
+ })
+ }
+
+ const toggleWinArchive = () => {
+ setShowWinArchive(s => !s)
+ }
+ return (
+ <>
+ toggleWinArchive()}>
+ {currentWinner.post_text}
+ by {currentWinner.player_name}
+
+
+
+ {showWinArchive ? : }
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/Pages/LeaderboardPage.jsx b/src/Pages/LeaderboardPage.jsx
new file mode 100644
index 0000000..77d0be9
--- /dev/null
+++ b/src/Pages/LeaderboardPage.jsx
@@ -0,0 +1,16 @@
+import { Box } from "@mui/material";
+import { useLoaderData, Await } from "react-router-dom";
+import React from "react";
+import Leaderboard from "../Components/Leaderboard";
+import LeaderboardSkeleton from "../Components/LeaderboardSkeleton";
+
+export default function LeaderboardPage() {
+ const { data } = useLoaderData()
+ return (
+ }>
+
+ {(users) => }
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/Pages/Login.jsx b/src/Pages/Login.jsx
new file mode 100644
index 0000000..fe348c1
--- /dev/null
+++ b/src/Pages/Login.jsx
@@ -0,0 +1,69 @@
+import TextField from '@mui/material/TextField'
+import LoadingButton from '@mui/lab/LoadingButton'
+import Box from '@mui/material/Box'
+import { Container } from '@mui/material'
+import Link from '@mui/material/Link'
+import { useState } from 'react'
+import { useNavigate } from "react-router-dom";
+import { loadUser } from '../utils'
+
+// TODO: change api key and token inputs to one input for Copy -> Request Headers
+
+export default function Login() {
+
+ const navigate = useNavigate()
+
+ const [ token, setToken ] = useState('')
+ const [ apikey, setApiKey ] = useState(localStorage.getItem("apikey"))
+ const [ failText, setFailText ] = useState('')
+ const [ apiKeyErr, setApiKeyErr ] = useState(false)
+ const [ tokenErr, setTokenErr ] = useState(false)
+ const [ loading, setLoading ] = useState(false)
+
+ const onTokenChange = (e) => setToken(e.target.value);
+ const onApiKeyChange = (e) => setApiKey(e.target.value);
+
+ const addUserToken = async () => {
+ setLoading(true)
+ // reset error values
+ setTokenErr(false)
+ setApiKeyErr(false)
+ // ensure the bearer token provided is valid
+ localStorage.setItem('token', token)
+ localStorage.setItem('apikey', apikey)
+ loadUser().then((r) => {
+ setLoading(false)
+ if (r.success) {
+ navigate('/')
+ } else {
+ if (r.apikeyInvalid) {
+ setApiKeyErr(true)
+ }
+ if (r.tokenInvalid) {
+ setTokenErr(true)
+ }
+ }
+ })
+ }
+
+ return (
+
+
+ Prittee is an experimental alternate front end for Pithee
+ Before using it is recommended you read the introduction to learn about Prittee's limitations
+ Add your Pithee api key and authorization token to start using Prittee.
+ You can find out how to get your info here.
+
+
+
+ Let's Go
+
+
+
+ )
+}
diff --git a/src/Pages/Profile.jsx b/src/Pages/Profile.jsx
new file mode 100644
index 0000000..3bec0b0
--- /dev/null
+++ b/src/Pages/Profile.jsx
@@ -0,0 +1,61 @@
+import Header from "../Components/Header";
+import Box from '@mui/material/Box'
+import { LinearProgress, CircularProgress } from "@mui/material";
+import { useLoaderData, Await } from "react-router-dom";
+import React from "react";
+import UserPost from "../Components/UserPost";
+
+export default function Profile() {
+ const { user, posts } = useLoaderData()
+
+ const loadingFallback = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+ return (
+
+ value)}>
+ {(value) => {
+ let [ user, posts ] = value
+ user = user.user
+ return(
+
+
+
+
+
+
+ {user.player_name}
+ Total Points: {user.total_points}
+ Posts: {posts.length}
+
+
+
+ {posts.map((post) => )}
+
+
+ )
+ }}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/contestants.js b/src/contestants.js
new file mode 100644
index 0000000..440d0bc
--- /dev/null
+++ b/src/contestants.js
@@ -0,0 +1,31 @@
+let contestants = {}
+
+const addContestant = function(post) {
+ contestants[post.id] = post
+}
+
+const rmContestant = function(post) {
+ delete contestants[post.id]
+}
+
+const lenContestants = function() {
+ return Object.keys(contestants).length
+}
+
+const getContestants = function() {
+ return Object.keys(contestants)
+}
+
+const getFullContestants = function() {
+ // when fetching the full contestants, the indices need
+ // to be reset to prevent duplication in the React renderer
+ let raw = Object.values(contestants)
+ let i = 0
+ for (let post of raw) {
+ post.index = i
+ i++
+ }
+ return raw
+}
+
+export { addContestant, rmContestant, lenContestants, getContestants, getFullContestants }
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ec2585e..578ae9f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +1,23 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
body {
margin: 0;
+ background-color: #141113;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ color: white;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+
+.centralContent {
+ margin: auto;
+}
diff --git a/src/index.js b/src/index.js
index d563c0f..5b9879e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,13 +2,94 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
+import Login from './Pages/Login'
+import Profile from './Pages/Profile'
+import CreatePost from './Pages/CreatePost'
+import LeaderboardPage from './Pages/LeaderboardPage';
import reportWebVitals from './reportWebVitals';
+import {
+ createBrowserRouter,
+ redirect,
+ RouterProvider,
+ defer
+} from 'react-router-dom'
+import { createTheme, ThemeProvider } from '@mui/material';
+import { lightBlue, cyan, orange, green, red, purple } from '@mui/material/colors';
+import CssBaseline from '@mui/material/CssBaseline';
+import { loadCurrentWinner, loadLeaderboardPage, loadUser, loadUserPosts, loadWinArchive } from './utils';
+import Index from './Pages/Index';
+
+const theme = createTheme({
+ palette: {
+ mode: 'dark',
+ primary: cyan,
+ secondary: purple,
+ error: red,
+ warning: orange,
+ info: lightBlue,
+ success: green,
+ },
+ typography: {
+ fontSize: 16,
+ }
+})
+const router = createBrowserRouter([
+ {
+ path: '/',
+ element: ,
+ children: [
+ {
+ index: true,
+ element: ,
+ loader: async () => {
+ let c = await loadCurrentWinner()
+ let a = await loadWinArchive()
+ if (!c || !a) {
+ return redirect('/login')
+ }
+
+ return new Promise((resolve) => resolve([a, c]))
+ },
+ },
+ {
+ path: '/me',
+ element: ,
+ loader: async () => {
+ return defer({
+ user: loadUser(),
+ posts: loadUserPosts()
+ })
+ }
+ },
+ {
+ path: '/post',
+ element:
+ },
+ {
+ path: '/leaderboard',
+ element: ,
+ loader: async () => {
+ return defer({
+ data: loadLeaderboardPage(1),
+ })
+ }
+ }
+ ]
+ },
+ {
+ path: '/login',
+ element: ,
+ },
+])
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-
-
-
+ <>
+
+
+
+
+ >
);
// If you want to start measuring performance in your app, pass a function
diff --git a/src/logo.svg b/src/logo.svg
deleted file mode 100644
index 9dfc1c0..0000000
--- a/src/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/queue.js b/src/queue.js
new file mode 100644
index 0000000..f39242d
--- /dev/null
+++ b/src/queue.js
@@ -0,0 +1,28 @@
+class Queue {
+ constructor() {
+ this.queue = [];
+ }
+
+ push(element) {
+ this.queue.push(element);
+ }
+
+ pop() {
+ return this.queue.shift();
+ }
+
+ peek() {
+ return this.queue[0];
+ }
+
+ print() {
+ console.log(this.queue)
+ }
+
+ size() {
+ return this.queue.length
+ }
+}
+
+export default Queue
+
\ No newline at end of file
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..305de8d
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,218 @@
+// NOTE: this file is poorly named
+import { addContestant, getContestants, rmContestant } from "./contestants"
+
+const userDataUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/get_player_data'
+const postsUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/get_posts'
+const playerPostsUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/get_player_posts'
+const currentWinnerUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/current_winner?select=*'
+const winnerArchiveUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/win_archive_view?select=*&limit=50'
+const ygsUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/ygs'
+const contestantsUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/update_contestants'
+const victorUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/update_recent_showdown'
+const leaderboardUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/get_paginated_leaderboard'
+const insertPostUrl = 'https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/insert_post'
+
+
+const tierIds = [
+ '1878f11f-782b-43fe-9aa3-cd34e989b174',
+ '629558be-c63d-4255-9bf5-1e98826fa3de',
+ '402261d8-4db8-4e24-8886-90dc6da6fcd1'
+]
+
+
+const loadUser = async function() {
+ return new Promise((resolve) => {
+ fetch('https://oqutjaxxxzzbjtyrfoka.supabase.co/rest/v1/rpc/get_player_data', {
+ method: 'POST', // why post?
+ headers: {
+ "APIKey": localStorage.getItem("apikey"),
+ "Authorization": localStorage.getItem("token")
+ }
+ }).then(r => r.json().then(data => ({status: r.status, body: data})))
+ .then((r) => {
+ // note: when an empty bearer token is provided, the server responds with 200 OK with an empty array as
+ // the body instead of the expected 401. This is a bug with Pithee that we need to account for!
+ if (r.body.length > 0) {
+ resolve({
+ success: true,
+ user: r.body[0]
+ })
+ } else if (r.status == 200) {
+ resolve({
+ success: false,
+ apikeyInvalid: false,
+ tokenInvalid: true,
+ })
+ } else {
+ resolve({
+ success: false,
+ apikeyInvalid: true,
+ tokenInvalid: true,
+ })
+ }
+ }).catch((err) => {
+ alert(err)
+ resolve({
+ success: false,
+ })
+ })
+ })
+}
+
+const loadUserPosts = async function () {
+ return makePitheeApiCall(playerPostsUrl, 'POST', null) // why POST?
+}
+
+const loadCurrentWinner = async function() {
+ return makePitheeApiCall(currentWinnerUrl, 'GET', null)
+}
+
+const loadWinArchive = async function() {
+ return makePitheeApiCall(winnerArchiveUrl, 'GET', null)
+}
+
+const fetchPosts = async function() {
+ return makePitheeApiCall(postsUrl, 'POST', JSON.stringify({post_quantity: 20}))
+}
+
+const loadLeaderboardPage = async function(page) {
+ return makePitheeApiCall(leaderboardUrl, 'POST', JSON.stringify({
+ page_number: page,
+ page_size: 25
+ }))
+}
+
+const updateContestants = async function() {
+ return makePitheeNoContentApiCall(contestantsUrl, 'POST', JSON.stringify({contestants: getContestants()}))
+}
+
+const showdownVictor = async function(victor_id) {
+ return makePitheeNoContentApiCall(victorUrl, 'POST', JSON.stringify({showdown_victor: victor_id}))
+}
+
+const pushPostScore = async function(post) {
+ if (post.tier < 1 || post.tier > 3) {
+ console.log('invalid tier value')
+ return
+ }
+
+ // just going to leave this unattended. sure I should check if the call
+ // failed, but I won't. Maybe I'll fix it later
+ makePitheeNoContentApiCall(ygsUrl, 'POST', JSON.stringify({
+ jacks: post.id,
+ films: tierIds[post.tier-1]
+ })) // good one, guys
+
+ // also update contestants for a three star post
+ // what confuses me is that the contestants for a showdown are stored
+ // client-side, but we still need to push them to the server. why?
+ // if you ask me, the contestants should be all on the server or on
+ // the client. IMO all on the server is better because I don't think
+ // showdown progress is retained upon refresh or login/logout which is silly
+ if (post.tier === 3) {
+ addContestant(post)
+ updateContestants()
+ }
+
+}
+
+const declareVictor = function (post) {
+ // upon showdown victor, another ygs call is made with the tier3 uuid,
+ // as well as a call to update_recent_showdown
+ // then, ONLY the victor is removed from the contestants
+ // this means that the very next post to reach tier3 triggers a showdown
+ // is this intended behavior? idk, but I will mirror it.
+ makePitheeNoContentApiCall(ygsUrl, 'POST', JSON.stringify({
+ jacks: post.id,
+ films: tierIds[2]
+ })) // good one, guys
+ showdownVictor(post.id)
+ rmContestant(post)
+ updateContestants()
+}
+
+const insertPost = function (postText) {
+ // the /insert_post endpoint weirdly responds with a string instead of
+ // JSON on success so I can't use the regular makePitheeApiCall function
+ return makePitheeNoContentApiCall(insertPostUrl, 'POST', JSON.stringify({
+ post_text: postText
+ }))
+}
+
+// /ygs and /update_contestants endpoints do not serve JSON responses so we need this func
+// ALSO both of those endpoints return 204 OK when a null authorization is given... ???
+const makePitheeNoContentApiCall = async function(url, method, body) {
+ return new Promise((resolve) => {
+ fetch(url, {
+ method: method,
+ headers: {
+ "apikey": localStorage.getItem('apikey'),
+ "authorization": localStorage.getItem('token'),
+ "Content-Type": "application/json"
+ },
+ body: body
+ })
+ .then((r) => {
+ if (r.status >= 200 && r.status <= 299) { // OK range
+ resolve(r.body)
+ } else if (r.status === 401) {
+ alert("Your token has expired. Please re-enter your information to keep using Prittee.")
+ window.location.href = '/login'
+ } else {
+ // TODO: this is little information for the client.
+ // the server could be responding with a 401 due to expired
+ // key (expected behavior) OR it could be a 500 server error and the client
+ // will not know the difference. This should be fixed!
+ resolve(null)
+ }
+ })
+ })
+}
+
+// the API annoyingly returns a 200 OK with an empty array response body
+// when called with a null api key...
+const makePitheeApiCall = async function(url, method, body) {
+ return new Promise((resolve) => {
+ fetch(url, {
+ method: method,
+ headers: {
+ "apikey": localStorage.getItem('apikey'),
+ "authorization": localStorage.getItem('token'),
+ "Content-Type": "application/json"
+ },
+ body: body
+ })
+ .then(r => r.json().then(data => ({status: r.status, body: data})))
+ .then((r) => {
+ if (r.status >= 200 && r.status <= 299) { // OK range
+ // check if empty array is given (token is null)
+ if (r.body.length == 0) {
+ alert("Your token has expired. Please re-enter your information to keep using Prittee.")
+ window.location.href = '/login'
+ }
+ resolve(r.body)
+ } else if (r.status === 401) {
+ alert("Your token has expired. Please re-enter your information to keep using Prittee.")
+ window.location.href = '/login'
+ } else {
+ // TODO: this is little information for the client.
+ // the server could be responding with a 401 due to expired
+ // key (expected behavior) OR it could be a 500 server error and the client
+ // will not know the difference. This should be fixed!
+ resolve(null)
+ }
+ })
+ })
+}
+
+export {
+ loadUser,
+ loadWinArchive,
+ loadCurrentWinner,
+ fetchPosts,
+ pushPostScore,
+ declareVictor,
+ loadUserPosts,
+ loadLeaderboardPage,
+ insertPost,
+}
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..3f0b66b
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,16 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ["./src/**/*.{js,jsx,ts,tsx}"],
+ theme: {
+ extend: {
+ backgroundSize: {
+ 'size-200': '200% 200%',
+ },
+ backgroundPosition: {
+ 'pos-0': '0% 0%',
+ 'pos-100': '100% 100%',
+ },
+ },
+ },
+ plugins: [],
+};
diff --git a/yarn.lock b/yarn.lock
index 5a43d04..f5cd2a3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -174,7 +174,7 @@
dependencies:
"@babel/types" "^7.23.0"
-"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3":
+"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3":
version "7.24.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128"
integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==
@@ -1112,7 +1112,7 @@
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
-"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.23.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.24.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57"
integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==
@@ -1269,6 +1269,113 @@
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016"
integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==
+"@emotion/babel-plugin@^11.11.0":
+ version "11.11.0"
+ resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
+ integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/runtime" "^7.18.3"
+ "@emotion/hash" "^0.9.1"
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/serialize" "^1.1.2"
+ babel-plugin-macros "^3.1.0"
+ convert-source-map "^1.5.0"
+ escape-string-regexp "^4.0.0"
+ find-root "^1.1.0"
+ source-map "^0.5.7"
+ stylis "4.2.0"
+
+"@emotion/cache@^11.11.0":
+ version "11.11.0"
+ resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
+ integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
+ dependencies:
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/sheet" "^1.2.2"
+ "@emotion/utils" "^1.2.1"
+ "@emotion/weak-memoize" "^0.3.1"
+ stylis "4.2.0"
+
+"@emotion/hash@^0.9.1":
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43"
+ integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==
+
+"@emotion/is-prop-valid@^1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337"
+ integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==
+ dependencies:
+ "@emotion/memoize" "^0.8.1"
+
+"@emotion/memoize@^0.8.1":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
+ integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
+
+"@emotion/react@^11.11.4":
+ version "11.11.4"
+ resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.4.tgz#3a829cac25c1f00e126408fab7f891f00ecc3c1d"
+ integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.11.0"
+ "@emotion/cache" "^11.11.0"
+ "@emotion/serialize" "^1.1.3"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
+ "@emotion/utils" "^1.2.1"
+ "@emotion/weak-memoize" "^0.3.1"
+ hoist-non-react-statics "^3.3.1"
+
+"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3", "@emotion/serialize@^1.1.4":
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.4.tgz#fc8f6d80c492cfa08801d544a05331d1cc7cd451"
+ integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==
+ dependencies:
+ "@emotion/hash" "^0.9.1"
+ "@emotion/memoize" "^0.8.1"
+ "@emotion/unitless" "^0.8.1"
+ "@emotion/utils" "^1.2.1"
+ csstype "^3.0.2"
+
+"@emotion/sheet@^1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
+ integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
+
+"@emotion/styled@^11.11.5":
+ version "11.11.5"
+ resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.5.tgz#0c5c8febef9d86e8a926e663b2e5488705545dfb"
+ integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.11.0"
+ "@emotion/is-prop-valid" "^1.2.2"
+ "@emotion/serialize" "^1.1.4"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1"
+ "@emotion/utils" "^1.2.1"
+
+"@emotion/unitless@^0.8.1":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3"
+ integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==
+
+"@emotion/use-insertion-effect-with-fallbacks@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963"
+ integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==
+
+"@emotion/utils@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4"
+ integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==
+
+"@emotion/weak-memoize@^0.3.1":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
+ integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
+
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -1301,6 +1408,33 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
+"@floating-ui/core@^1.0.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
+ integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==
+ dependencies:
+ "@floating-ui/utils" "^0.2.1"
+
+"@floating-ui/dom@^1.6.1":
+ version "1.6.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef"
+ integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==
+ dependencies:
+ "@floating-ui/core" "^1.0.0"
+ "@floating-ui/utils" "^0.2.0"
+
+"@floating-ui/react-dom@^2.0.8":
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.8.tgz#afc24f9756d1b433e1fe0d047c24bd4d9cefaa5d"
+ integrity sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==
+ dependencies:
+ "@floating-ui/dom" "^1.6.1"
+
+"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
+ integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
+
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
@@ -1629,6 +1763,110 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1"
integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==
+"@mui/base@5.0.0-beta.40":
+ version "5.0.0-beta.40"
+ resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.40.tgz#1f8a782f1fbf3f84a961e954c8176b187de3dae2"
+ integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@floating-ui/react-dom" "^2.0.8"
+ "@mui/types" "^7.2.14"
+ "@mui/utils" "^5.15.14"
+ "@popperjs/core" "^2.11.8"
+ clsx "^2.1.0"
+ prop-types "^15.8.1"
+
+"@mui/core-downloads-tracker@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz#f7c57b261904831877220182303761c012d05046"
+ integrity sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==
+
+"@mui/icons-material@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.14.tgz#333468c94988d96203946d1cfeb8f4d7e8e7de34"
+ integrity sha512-vj/51k7MdFmt+XVw94sl30SCvGx6+wJLsNYjZRgxhS6y3UtnWnypMOsm3Kmg8TN+P0dqwsjy4/fX7B1HufJIhw==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+
+"@mui/lab@^5.0.0-alpha.169":
+ version "5.0.0-alpha.169"
+ resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.169.tgz#85b88b2f06ad78c586cde2b47970653e5fd895eb"
+ integrity sha512-h6xe1K6ISKUbyxTDgdvql4qoDP6+q8ad5fg9nXQxGLUrIeT2jVrBuT/jRECSTufbnhzP+V5kulvYxaMfM8rEdA==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/base" "5.0.0-beta.40"
+ "@mui/system" "^5.15.14"
+ "@mui/types" "^7.2.14"
+ "@mui/utils" "^5.15.14"
+ clsx "^2.1.0"
+ prop-types "^15.8.1"
+
+"@mui/material@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.14.tgz#a40bd5eccfa9fc925535e1f4d70c6cef77fa3a75"
+ integrity sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/base" "5.0.0-beta.40"
+ "@mui/core-downloads-tracker" "^5.15.14"
+ "@mui/system" "^5.15.14"
+ "@mui/types" "^7.2.14"
+ "@mui/utils" "^5.15.14"
+ "@types/react-transition-group" "^4.4.10"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+ react-is "^18.2.0"
+ react-transition-group "^4.4.5"
+
+"@mui/private-theming@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.14.tgz#edd9a82948ed01586a01c842eb89f0e3f68970ee"
+ integrity sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/utils" "^5.15.14"
+ prop-types "^15.8.1"
+
+"@mui/styled-engine@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.14.tgz#168b154c4327fa4ccc1933a498331d53f61c0de2"
+ integrity sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@emotion/cache" "^11.11.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+
+"@mui/system@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.14.tgz#8a0c6571077eeb6b5f1ff7aa7ff6a3dc4a14200d"
+ integrity sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@mui/private-theming" "^5.15.14"
+ "@mui/styled-engine" "^5.15.14"
+ "@mui/types" "^7.2.14"
+ "@mui/utils" "^5.15.14"
+ clsx "^2.1.0"
+ csstype "^3.1.3"
+ prop-types "^15.8.1"
+
+"@mui/types@^7.2.14":
+ version "7.2.14"
+ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.14.tgz#8a02ac129b70f3d82f2f9b76ded2c8d48e3fc8c9"
+ integrity sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==
+
+"@mui/utils@^5.15.14":
+ version "5.15.14"
+ resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.14.tgz#e414d7efd5db00bfdc875273a40c0a89112ade3a"
+ integrity sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==
+ dependencies:
+ "@babel/runtime" "^7.23.9"
+ "@types/prop-types" "^15.7.11"
+ prop-types "^15.8.1"
+ react-is "^18.2.0"
+
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
@@ -1677,6 +1915,16 @@
schema-utils "^3.0.0"
source-map "^0.7.3"
+"@popperjs/core@^2.11.8":
+ version "2.11.8"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
+ integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
+
+"@remix-run/router@1.15.3":
+ version "1.15.3"
+ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.3.tgz#d2509048d69dbb72d5389a14945339f1430b2d3c"
+ integrity sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==
+
"@rollup/plugin-babel@^5.2.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
@@ -2120,7 +2368,7 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f"
integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==
-"@types/prop-types@*":
+"@types/prop-types@*", "@types/prop-types@^15.7.11":
version "15.7.12"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6"
integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==
@@ -2147,6 +2395,13 @@
dependencies:
"@types/react" "*"
+"@types/react-transition-group@^4.4.10":
+ version "4.4.10"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
+ integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==
+ dependencies:
+ "@types/react" "*"
+
"@types/react@*":
version "18.2.73"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.73.tgz#0579548ad122660d99e00499d22e33b81e73ed94"
@@ -2815,7 +3070,7 @@ at-least-node@^1.0.0:
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
-autoprefixer@^10.4.13:
+autoprefixer@^10.4.13, autoprefixer@^10.4.19:
version "10.4.19"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f"
integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==
@@ -3258,6 +3513,11 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
+clsx@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
+ integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
+
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -3400,7 +3660,7 @@ content-type@~1.0.4, content-type@~1.0.5:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
-convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
@@ -3668,7 +3928,7 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
-csstype@^3.0.2:
+csstype@^3.0.2, csstype@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
@@ -3912,6 +4172,14 @@ dom-converter@^0.2.0:
dependencies:
utila "~0.4"
+dom-helpers@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
dom-serializer@0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -4723,6 +4991,11 @@ find-cache-dir@^3.3.1:
make-dir "^3.0.2"
pkg-dir "^4.1.0"
+find-root@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+ integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@@ -5096,6 +5369,13 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+hoist-non-react-statics@^3.3.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+ integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+ dependencies:
+ react-is "^16.7.0"
+
hoopy@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
@@ -7711,7 +7991,7 @@ postcss@^7.0.35:
picocolors "^0.2.1"
source-map "^0.6.1"
-postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4:
+postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.4:
version "8.4.38"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
@@ -7791,7 +8071,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7924,7 +8204,7 @@ react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
-react-is@^16.13.1:
+react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -7934,7 +8214,7 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-is@^18.0.0:
+react-is@^18.0.0, react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
@@ -7944,6 +8224,21 @@ react-refresh@^0.11.0:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
+react-router-dom@^6.22.3:
+ version "6.22.3"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.3.tgz#9781415667fd1361a475146c5826d9f16752a691"
+ integrity sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==
+ dependencies:
+ "@remix-run/router" "1.15.3"
+ react-router "6.22.3"
+
+react-router@6.22.3:
+ version "6.22.3"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.3.tgz#9d9142f35e08be08c736a2082db5f0c9540a885e"
+ integrity sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==
+ dependencies:
+ "@remix-run/router" "1.15.3"
+
react-scripts@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"
@@ -7999,6 +8294,16 @@ react-scripts@5.0.1:
optionalDependencies:
fsevents "^2.3.2"
+react-transition-group@^4.4.5:
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
+ integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -8565,6 +8870,11 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+source-map@^0.5.7:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
+
source-map@^0.7.3:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
@@ -8818,6 +9128,11 @@ stylehacks@^5.1.1:
browserslist "^4.21.4"
postcss-selector-parser "^6.0.4"
+stylis@4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
+ integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
+
sucrase@^3.32.0:
version "3.35.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
@@ -8907,7 +9222,7 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
-tailwindcss@^3.0.2:
+tailwindcss@^3.0.2, tailwindcss@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519"
integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==