From 9b5a44bdc6f1aa08b61ae70e9fd8cc0a24d8cc60 Mon Sep 17 00:00:00 2001 From: Henry Hobbs Date: Wed, 19 Jun 2024 17:17:05 -0400 Subject: [PATCH] Added about page and small styling tweaks --- app/about/page.tsx | 56 ++++++++ app/components/header.tsx | 23 ++++ app/layout.tsx | 15 ++- app/page.tsx | 170 ++++++++++++----------- package-lock.json | 275 ++++++++++++++++++++++++++++++++++++++ package.json | 11 +- 6 files changed, 469 insertions(+), 81 deletions(-) create mode 100644 app/about/page.tsx create mode 100644 app/components/header.tsx diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..4ec0a4f --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,56 @@ +import { Sheet, Typography } from "@mui/joy" + +export default function About() { + return ( + + + About this site + + + Inspired by a fried's need to build a deck using cards featuring chairs, + this search engine helps you find Magic: The Gathering cards by their + artwork. + + + Azure Cognitive Services was used to analyze the images and generate + captions, which are then compared against the search query. Card data + and images are from{" "} + + Scryfall + + . + + + This site was built by{" "} + + Henry Hobbs + + . Site source is available{" "} + + here + {" "} + and the scripts used to generate the card data are available{" "} + + here + + . + + + Questions? Feature requests? Cards out of date? Email me at{" "} + + mtg@hobbs.zone + + . + + + ) +} diff --git a/app/components/header.tsx b/app/components/header.tsx new file mode 100644 index 0000000..5fb5486 --- /dev/null +++ b/app/components/header.tsx @@ -0,0 +1,23 @@ +import { Sheet, Typography } from "@mui/joy" +import Link from "next/link" + +export default function Header() { + return ( + + + MTG Visual Search + + + About + + + ) +} diff --git a/app/layout.tsx b/app/layout.tsx index 9ee7b3f..50af838 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,8 @@ import type { Metadata } from "next" import { Inter } from "next/font/google" import "./globals.css" +import Header from "./components/header" +import { Sheet } from "@mui/joy" const inter = Inter({ subsets: ["latin"] }) @@ -16,7 +18,18 @@ export default function RootLayout({ }>) { return ( - {children} + + +
+ {children} + + ) } diff --git a/app/page.tsx b/app/page.tsx index 483fc21..45c3e51 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,22 +4,28 @@ import { Button, CircularProgress, Grid, + IconButton, Input, - Sheet, 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" export default function Home() { const [searchQuery, setSearchQuery] = useState("") const [loading, setLoading] = useState(false) const [cards, setCards] = useState([]) const [cardsDisplayed, setCardsDisplayed] = useState([]) + const handleSubmit = async (e: FormEvent) => { e.preventDefault() + if (!searchQuery) { + handleClear() + return + } setLoading(true) const response = await fetch(`/api/?search=${searchQuery}`) const data = await response.json() @@ -27,47 +33,60 @@ export default function Home() { setCardsDisplayed(data.slice(0, 12)) setLoading(false) } + + const handleClear = () => { + setCards([]) + setCardsDisplayed([]) + setSearchQuery("") + } + const loadMore = async () => { if (cardsDisplayed.length >= cards.length) return setCardsDisplayed( cards.slice(0, Math.min(cardsDisplayed.length + 12, cards.length)) ) } + return ( - - + Search MTG Artwork + + For best results use simple words, separated by spaces. + + + Search will return all cards that match at least one of the words, + so use many similar words to broaden your search. + + + )} +
- {!cards?.length && ( - <> - Search MTG Artwork - - For best results use simple words, separated by spaces. - - - Search will return all cards that match at least one of the words, - so use many similar words to broaden your search. - - - )} - - setSearchQuery(e.target.value)} - endDecorator={ + setSearchQuery(e.target.value)} + endDecorator={ + <> + {!!cards?.length && ( + + + + )} - } - sx={{ width: "100%" }} // Add this line to make the search bar full width - /> -
- {!!cards?.length && ( - } - style={{ width: "100%" }} - > - - {cardsDisplayed.map((card) => ( - - - {card.name} - ((e.target as HTMLImageElement).style.transform = - "scale(1.1)") - } - onMouseOut={(e) => - ((e.target as HTMLImageElement).style.transform = - "scale(1)") - } - /> - - - ))} - - - )} -
-
+ + } + sx={{ width: "100%" }} // Add this line to make the search bar full width + /> + + {!cards?.length &&
} + {!!cards?.length && ( + } + style={{ width: "100%" }} + > + + {cardsDisplayed.map((card) => ( + + + {card.name} + ((e.target as HTMLImageElement).style.transform = + "scale(1.1)") + } + onMouseOut={(e) => + ((e.target as HTMLImageElement).style.transform = + "scale(1)") + } + /> + + + ))} + + + )} + ) } diff --git a/package-lock.json b/package-lock.json index d5270b4..71a55cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@mui/icons-material": "^5.15.20", "@mui/joy": "^5.0.0-beta.36", "@types/react-infinite-scroller": "^1.2.5", "next": "14.2.3", @@ -308,6 +309,31 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.20.tgz", + "integrity": "sha512-oGcKmCuHaYbAAoLN67WKSXtHmEgyWcJToT1uRtmPyxMj9N5uqwc/mRtEnst4Wj/eGr+zYH2FiZQ79v9k7kSk1Q==", + "dependencies": { + "@babel/runtime": "^7.23.9" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/joy": { "version": "5.0.0-beta.36", "resolved": "https://registry.npmjs.org/@mui/joy/-/joy-5.0.0-beta.36.tgz", @@ -348,6 +374,220 @@ } } }, + "node_modules/@mui/material": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.20.tgz", + "integrity": "sha512-tVq3l4qoXx/NxUgIx/x3lZiPn/5xDbdTE8VrLczNpfblLYZzlrbxA7kb9mI8NoBF6+w9WE9IrxWnKK5KlPI2bg==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.20", + "@mui/system": "^5.15.20", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "@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" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "peer": true, + "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" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/core-downloads-tracker": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.20.tgz", + "integrity": "sha512-DoL2ppgldL16utL8nNyj/P12f8mCNdx/Hb/AJnX9rLY4b52hCMIx1kH83pbXQ6uMy6n54M3StmEbvSGoj2OFuA==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material/node_modules/@mui/private-theming": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.20.tgz", + "integrity": "sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.20", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/styled-engine": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/system": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.20.tgz", + "integrity": "sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.20", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.20", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/utils": { + "version": "5.15.20", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.20.tgz", + "integrity": "sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/private-theming": { "version": "6.0.0-alpha.8", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.0.0-alpha.8.tgz", @@ -692,6 +932,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "peer": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -827,6 +1076,16 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1154,6 +1413,22 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", diff --git a/package.json b/package.json index d4b3691..dd06a7b 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,20 @@ "lint": "next lint" }, "dependencies": { - "react": "^18", - "react-dom": "^18", - "next": "14.2.3", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@mui/icons-material": "^5.15.20", "@mui/joy": "^5.0.0-beta.36", "@types/react-infinite-scroller": "^1.2.5", + "next": "14.2.3", + "react": "^18", + "react-dom": "^18", "react-infinite-scroller": "^1.2.6" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", - "@types/react-dom": "^18" + "@types/react-dom": "^18", + "typescript": "^5" } }