mtg-visual-search/app/page.tsx

145 lines
4.2 KiB
TypeScript
Raw Normal View History

2024-05-24 01:16:02 +00:00
"use client"
import {
Button,
CircularProgress,
Grid,
IconButton,
2024-05-24 01:16:02 +00:00
Input,
Stack,
Typography,
} from "@mui/joy"
import { FormEvent, useState } from "react"
import { CardType } from "@/types/types"
import InfiniteScroll from "react-infinite-scroller"
import { Clear } from "@mui/icons-material"
2024-05-23 21:18:39 +00:00
export default function Home() {
2024-05-24 01:16:02 +00:00
const [searchQuery, setSearchQuery] = useState("")
const [loading, setLoading] = useState(false)
const [cards, setCards] = useState<CardType[]>([])
const [cardsDisplayed, setCardsDisplayed] = useState<CardType[]>([])
2024-05-24 01:16:02 +00:00
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
if (!searchQuery) {
handleClear()
return
}
2024-05-24 01:16:02 +00:00
setLoading(true)
const response = await fetch(`/api/?search=${searchQuery}`)
const data = await response.json()
setCards(data)
setCardsDisplayed(data.slice(0, 12))
setLoading(false)
}
const handleClear = () => {
setCards([])
setCardsDisplayed([])
setSearchQuery("")
}
2024-05-24 01:16:02 +00:00
const loadMore = async () => {
if (cardsDisplayed.length >= cards.length) return
setCardsDisplayed(
cards.slice(0, Math.min(cardsDisplayed.length + 12, cards.length))
)
}
2024-05-23 21:18:39 +00:00
return (
<Stack
spacing={2}
sx={{ margin: "auto", flex: 1, width: "100%", padding: 5 }}
alignItems="center"
justifyContent={cards?.length ? "flex-start" : "center"}
textAlign="center"
2024-05-24 01:16:02 +00:00
>
{!cards?.length && (
<>
<Typography level="h1">Search MTG Artwork</Typography>
<Typography level="h4">
For best results use simple words, separated by spaces.
</Typography>
<Typography>
Search will return all cards that match at least one of the words,
so use many similar words to broaden your search.
</Typography>
</>
)}
<form
onSubmit={handleSubmit}
style={{ width: "100%", maxWidth: "700px", paddingBottom: "16px" }}
2024-05-24 01:16:02 +00:00
>
<Input
placeholder="Type something..."
size="lg"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
endDecorator={
<>
{!!cards?.length && (
<IconButton
sx={{ marginRight: 0 }}
onClick={handleClear}
color="danger"
>
<Clear />
</IconButton>
)}
2024-05-24 01:16:02 +00:00
<Button
variant="solid"
color="primary"
size="lg"
type="submit"
loading={loading}
sx={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }}
>
Search
</Button>
</>
}
sx={{ width: "100%" }} // Add this line to make the search bar full width
/>
</form>
{!cards?.length && <div style={{ height: "10vh" }}></div>}
{!!cards?.length && (
<InfiniteScroll
pageStart={0}
initialLoad={false}
loadMore={loadMore}
hasMore={cardsDisplayed.length < cards.length}
loader={<CircularProgress />}
style={{ width: "100%" }}
>
<Grid container spacing={2} sx={{ flexGrow: 1 }} key="cardGrid">
{cardsDisplayed.map((card) => (
<Grid xs={12} sm={6} md={4} lg={3} xl={2} key={card.id}>
<a href={card.url} target="_blank" rel="noreferrer">
<img
src={card.mediumImageUrl}
alt={card.name}
style={{
width: "100%",
borderRadius: 16,
transition: "transform 0.1s",
}}
onMouseOver={(e) =>
((e.target as HTMLImageElement).style.transform =
"scale(1.1)")
}
onMouseOut={(e) =>
((e.target as HTMLImageElement).style.transform =
"scale(1)")
}
/>
</a>
</Grid>
))}
</Grid>
</InfiniteScroll>
)}
</Stack>
2024-05-24 01:16:02 +00:00
)
2024-05-23 21:18:39 +00:00
}