First Commit

pull/1/head
Gabe Farrell 2 years ago
parent d198a9ed8a
commit 229c967128

@ -1,70 +1,7 @@
# Getting Started with Create React App
# Prittee
![Prittee main page](image-2.png)
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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

@ -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"
}
}

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -3,11 +3,11 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="viewport" content="initial-scale=1, width=device-width" />
<meta
name="description"
content="Web site created using create-react-app"
content="An unofficial alternate front end for Pithee"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Prittee - A Pithee Client</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

@ -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",

@ -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 */
}

@ -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 (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
<Container>
<Box
display="flex"
flexDirection="column"
alignItems="center"
className='h-screen'
>
Learn React
</a>
</header>
</div>
<Header />
<Box width={480} id='gameContainer' className='grow'>
<Outlet />
</Box>
<Footer/>
</Box>
</Container>
);
}

@ -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 (
<Box className='prevent-select'>
{formatTime(seconds)}
</Box>
)
}

@ -0,0 +1,10 @@
import { Container, Box } from "@mui/material";
export default function Footer() {
return (
<Box width={600} className='flex flex-row justify-around text-sm text-neutral-500 border-t-2 border-neutral-600 p-4 mt-4'>
<a className="hover:text-neutral-300" href="https://github.com/gabehf/Prittee" target="_blank">View the source on GitHub</a>
<a className="hover:text-neutral-300" href="https://forms.gle/a8BkH6yUDnzLJD938" target="_blank">Report a bug</a>
</Box>
)
}

@ -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 (
<Container className='mt-6 mb-4'>
<Box id="header" className="mb-2 text-center hover:cursor-pointer" onClick={() => navigate('/')}>
<h1 className='text-4xl font-bold'>Prittee</h1>
<p className="text-md text-slate-300">An <span className='text-sky-400 italic'>unofficial</span> alternate front end for Pithee</p>
</Box>
<Box className='flex flex-row justify-between m-auto mb-6' width={125}>
<Box>
<NavLink to='/me'><AccountBoxOutlined /></NavLink>
</Box>
<Box>
<NavLink to='/leaderboard'><LeaderboardOutlined /></NavLink>
</Box>
<Box>
<NavLink to='/post'><PostAdd /></NavLink>
</Box>
</Box>
</Container>
)
}

@ -0,0 +1,21 @@
import { Box } from "@mui/material";
export default function Leaderboard({ users }) {
let i = 0
return (
<Box maxHeight={500} className='overflow-y-auto no-scrollbar'>
{users.map((user) => {
i++
return (
<Box className='flex flex-row justify-between border-b-2 p-2 mb-3'>
<Box className='flex flex-row gap-8'>
<Box>{i}.</Box>
<img src={user.avatar_url} alt={user.player_name} width={35} height={35}/>
</Box>
<Box>{user.player_name}</Box>
<Box className='text-end'>{user.total_points}</Box>
</Box>
)})}
</Box>
)
}

@ -0,0 +1,9 @@
import { LinearProgress, Box } from "@mui/material";
export default function LeaderboardSkeleton() {
return (
<Box maxHeight={500} className='overflow-y-auto no-scrollbar'>
<LinearProgress color="inherit" />
</Box>
)
}

@ -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(
<Star fontSize='small' />
)
}
return arr
}
if (text === '') {
return (
<Box className="
transition-all
duration-300
bg-gradient-to-tl
from-indigo-900
via-indigo-600
to-purple-500
bg-size-200
bg-pos-0
hover:bg-pos-100
hover:cursor-pointer
p-8" height={225}>
<LinearProgress sx={{
backgroundColor: 'white',
'& .MuiLinearProgress-bar': {
backgroundColor: 'indigo'
}
}} className="opacity-25"/>
</Box>
)
} else {
return (
<Box className="
transition-all
duration-300
bg-gradient-to-tl
from-indigo-900
via-indigo-600
to-purple-500
bg-size-200
bg-pos-0
hover:bg-pos-100
hover:cursor-pointer
p-8" height={225} onClick={() => onClick({post_text: text, index: index, tier: tier, id: id})}>
<Box sx={{marginTop: -2, marginBottom: 1, marginLeft: -2, display: 'flex'}}>
{renderStars()}
</Box>
<p className='text-clip overflow-hidden prevent-select'>{text}</p>
</Box>
)
}
}

@ -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 (
<Grid container spacing={1}>
{posts.map((p) => {
return(
<Grid item xs={6} key={'post-grid-'+p.index}>
<Post text={p.post_text} key={p.id} id={p.id} tier={p.tier} index={p.index} onClick={selectPost}/>
</Grid>
)})}
</Grid>
)
}

@ -0,0 +1,12 @@
import { Box } from "@mui/material";
export default function PreviousWinner({ post }) {
let dateString = new Date(post.won_at).toLocaleString()
return (
<Box className='border-b-2 border-white p-4'>
<Box>{post.post_text}</Box>
<Box className='text-sm'>by {post.player_name}</Box>
<Box className='text-sm'>Won: {dateString}</Box>
</Box>
)
}

@ -0,0 +1,14 @@
import { Box } from "@mui/material";
export default function UserPost({ post }) {
let dateString = new Date(post.created_at).toLocaleString()
return (
<Box className='border-b-2 border-white p-4'>
<Box>{post.post_text}</Box>
<Box className='text-sm'>Points: {post.points}</Box>
<Box className='text-sm'>Posted: {dateString}</Box>
{/* I actually don't know if won_at is the right key because i haven't won yet LOL */}
{post.won_at ? <Box className='text-sm'>Posted: {dateString}</Box> : <></>}
</Box>
)
}

@ -0,0 +1,15 @@
import { Box } from "@mui/material";
import PreviousWinner from "./PreviousWinner";
export default function WinArchive({ data }) {
return(
<Box id="lastWinner" className="
p-4
pt-0
mb-6
overflow-y-auto
no-scrollbar" height={434}>
{data.map((post) => <PreviousWinner post={post}/>)}
</Box>
)
}

@ -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 (
<Box className='flex flex-col align-end m-auto' width={300}>
<TextField color={text.length > 80 ? 'error' : 'primary'} label="Make a post" multiline rows={3} margin='normal' onChange={(e) => setText(e.target.value)}/>
<div className={text.length > 80 ? 'text-red-400' : 'text-neutral-300'}>{text.length}/80</div>
<LoadingButton loading={loading} className='grow-0' onClick={submitHandler}>Submit</LoadingButton>
</Box>
)
}
//i saw a bank that offered "24 hour service" and I thought "it takes that long??"

@ -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 (
<>
<Box id="lastWinner" className="
bg-gradient-to-tl
transition-all
duration-300
from-purple-500
via-sky-500
to-emerald-500
bg-size-200
bg-pos-0
hover:bg-pos-100
hover:cursor-pointer
p-8
mb-3" onClick={() => toggleWinArchive()}>
<p className="post-text pb-2 prevent-select" id="winnerPostText">{currentWinner.post_text}</p>
<p className="post-author prevent-select" id="winnerPostAuthor">by {currentWinner.player_name}</p>
<Countdown key={String(winnerTime)} until={winnerTime} onEnd={reloadWinner}/>
</Box>
<Box id="posts">
{showWinArchive ? <WinArchive data={winArchive}></WinArchive>: <PostGrid />}
</Box>
</>
)
}

@ -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 (
<React.Suspense fallback={<LeaderboardSkeleton />}>
<Await resolve={data}>
{(users) => <Leaderboard users={users} />}
</Await>
</React.Suspense>
)
}

@ -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 (
<Box
display="flex"
justifyContent="center"
alignItems="center"
minHeight="100vh"
>
<Container>
<h1 className='text-center mb-12 text-xl'>Prittee is an <span className='text-red-400 italic'>experimental</span> alternate front end for Pithee<br/>
Before using it is recommended you <Link href='https://zircon-stoplight-fbb.notion.site/Prittee-Introduction-0e418deda00242ebb6026d074b397629'>read the introduction</Link> to learn about Prittee's limitations</h1>
<p className='text-center'>Add your Pithee api key and authorization token to start using Prittee.<br/>
You can find out how to get your info <Link href="https://zircon-stoplight-fbb.notion.site/Prittee-Introduction-0e418deda00242ebb6026d074b397629">here</Link>.</p>
<Box component="div" display="flex" flexDirection="row" alignItems="center" justifyContent="center" gap={4} className='mt-6'>
<TextField id="apikey" error={apiKeyErr} onChange={onApiKeyChange} label="API Key" variant="outlined" value={localStorage.getItem("apikey")} />
<TextField id="auth-token" error={tokenErr} helperText={failText} onChange={onTokenChange} label="Authorization Token" variant="outlined" />
<LoadingButton loading={loading} variant="text" size="large" onClick={addUserToken}>Let's Go</LoadingButton>
</Box>
</Container>
</Box>
)
}

@ -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 (
<Box className='flex flex-col items-center'>
<Box className='flex flex-row justify-around items-center' width={480} height={140}>
<Box maxWidth={150}>
<CircularProgress color="inherit"/>
</Box>
<Box width={100}>
<LinearProgress color="inherit" />
<br/>
<LinearProgress color="inherit" />
<br/>
<LinearProgress color="inherit" />
</Box>
</Box>
<Box maxHeight={400} width={400} className='mt-4 overflow-y-auto no-scrollbar'>
<br/>
<br/>
<LinearProgress color="inherit" />
</Box>
</Box>
)
}
return (
<React.Suspense fallback={loadingFallback()}>
<Await resolve={Promise.all([user, posts]).then(value => value)}>
{(value) => {
let [ user, posts ] = value
user = user.user
return(
<Box className='flex flex-col items-center'>
<Box className='flex flex-row justify-around items-center' width={480}>
<Box maxWidth={150}>
<img src={user.avatar_url} alt={`${user.player_name}`} />
</Box>
<Box>
<h2 className="text-2xl font-bold mb-2">{user.player_name}</h2>
<p>Total Points: {user.total_points}</p>
<p>Posts: {posts.length}</p>
</Box>
</Box>
<Box maxHeight={400} className='mt-4 overflow-y-auto no-scrollbar'>
{posts.map((post) => <UserPost post={post} />)}
</Box>
</Box>
)
}}
</Await>
</React.Suspense>
)
}

@ -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 }

@ -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;
}

@ -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: <App />,
children: [
{
index: true,
element: <Index />,
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: <Profile />,
loader: async () => {
return defer({
user: loadUser(),
posts: loadUserPosts()
})
}
},
{
path: '/post',
element: <CreatePost />
},
{
path: '/leaderboard',
element: <LeaderboardPage />,
loader: async () => {
return defer({
data: loadLeaderboardPage(1),
})
}
}
]
},
{
path: '/login',
element: <Login />,
},
])
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
<>
<ThemeProvider theme={theme}>
<CssBaseline />
<RouterProvider router={router} />
</ThemeProvider>
</>
);
// If you want to start measuring performance in your app, pass a function

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

@ -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

@ -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,
}

@ -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: [],
};

@ -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==

Loading…
Cancel
Save