Show dummy search results.
parent
bb650f6d35
commit
99b9519ab7
|
@ -1,13 +1,18 @@
|
|||
import * as React from "react"
|
||||
import { RouterProvider } from "react-router-dom"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
|
||||
import { RootLayout } from "./components/RootLayout"
|
||||
import { router } from "./router"
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RootLayout>
|
||||
<RouterProvider router={router} />
|
||||
</RootLayout>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import * as React from "react"
|
||||
|
||||
interface CaptionImageProps {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
src: string
|
||||
}
|
||||
|
||||
export function CaptionImage({ title, subtitle, src }: CaptionImageProps) {
|
||||
return (
|
||||
<div className="group relative h-96 overflow-hidden rounded-3xl bg-neutral-100">
|
||||
<img
|
||||
alt=""
|
||||
src={src}
|
||||
className="h-full w-full object-cover transition duration-500 motion-safe:group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black to-black/0 to-40% p-6">
|
||||
{title && (
|
||||
<p className="font-display text-base/6 font-semibold tracking-wide text-white">
|
||||
{title}
|
||||
</p>
|
||||
)}
|
||||
{subtitle && <p className="mt-2 text-sm text-white">{subtitle}</p>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react"
|
||||
import clsx from "clsx"
|
||||
|
||||
import type { Query } from "../types/Query"
|
||||
import type { SearchParams } from "../types/SearchParams"
|
||||
|
||||
import FilterIcon from "../icons/Filter"
|
||||
import RightArrowIcon from "../icons/RightArrow"
|
||||
|
@ -11,8 +11,8 @@ import { Button } from "./Button"
|
|||
interface FilterOption {
|
||||
title: string
|
||||
Icon: ({ ...props }: { [x: string]: any }) => React.JSX.Element
|
||||
enable: (q: Query) => Query
|
||||
isEnabled: (q: Query) => boolean
|
||||
enable: (p: SearchParams) => SearchParams
|
||||
isEnabled: (p: SearchParams) => boolean
|
||||
}
|
||||
|
||||
const filters: FilterOption[] = [
|
||||
|
@ -33,12 +33,12 @@ enum Direction {
|
|||
}
|
||||
|
||||
interface FilterScrollProps {
|
||||
query: Query
|
||||
params: SearchParams
|
||||
onModal: () => void
|
||||
onEnable: (q: Query) => void
|
||||
onSelect: (p: SearchParams) => void
|
||||
}
|
||||
|
||||
export function FilterScroll({ query, onModal, onEnable }: FilterScrollProps) {
|
||||
export function FilterScroll({ params, onModal, onSelect }: FilterScrollProps) {
|
||||
const viewport = React.useRef<HTMLDivElement>(null)
|
||||
const [isFlush, setIsFlush] = React.useState([true, false])
|
||||
|
||||
|
@ -67,9 +67,9 @@ export function FilterScroll({ query, onModal, onEnable }: FilterScrollProps) {
|
|||
<div
|
||||
key={e.title}
|
||||
className={clsx("flex-none cursor-pointer text-center", {
|
||||
"fill-amber-500 text-amber-500": e.isEnabled(query),
|
||||
"fill-amber-500 text-amber-500": e.isEnabled(params),
|
||||
})}
|
||||
onClick={() => onEnable(e.enable({ ...query }))}
|
||||
onClick={() => onSelect(e.enable({ ...params }))}
|
||||
>
|
||||
<e.Icon className="mx-auto h-6 w-6" />
|
||||
<span className="text-xs">{e.title}</span>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import * as React from "react"
|
||||
import clsx from "clsx"
|
||||
|
||||
type SearchResultProps = {
|
||||
title?: string
|
||||
subtitle?: string
|
||||
src?: string
|
||||
} & React.ComponentPropsWithoutRef<"div">
|
||||
|
||||
export function SearchResult({
|
||||
title,
|
||||
subtitle,
|
||||
src,
|
||||
className,
|
||||
...props
|
||||
}: SearchResultProps) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"group relative h-96 overflow-hidden rounded-3xl bg-neutral-100",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
className="h-full w-full object-cover transition duration-500 motion-safe:group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black to-black/0 to-40% p-6">
|
||||
{title ? (
|
||||
<p className="font-display text-base/6 font-semibold tracking-wide text-white">
|
||||
{title}
|
||||
</p>
|
||||
) : null}
|
||||
{subtitle ? (
|
||||
<p className="mt-2 text-sm text-white">{subtitle}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,63 +1,68 @@
|
|||
import * as React from "react"
|
||||
import axios from "axios"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
|
||||
import type { Query } from "../types/Query"
|
||||
import type { Coach } from "../types/Coach"
|
||||
import { type SearchParams, defaultSearchParams } from "../types/SearchParams"
|
||||
|
||||
import { CaptionImage } from "../components/CaptionImage"
|
||||
import { Container } from "../components/Container"
|
||||
import { FadeIn, FadeInStagger } from "../components/FadeIn"
|
||||
import { FallbackMessage } from "../components/FallbackMessage"
|
||||
import { FilterScroll } from "../components/FilterScroll"
|
||||
import { Loading } from "../components/Loading"
|
||||
import { SearchResult } from "../components/SearchResult"
|
||||
|
||||
const FIDE_RATING_MIN = 1500
|
||||
const FIDE_RATING_MAX = 3200
|
||||
function SearchResults() {
|
||||
const { isLoading, isError, data } = useQuery({
|
||||
queryKey: ["coaches"],
|
||||
queryFn: async () => {
|
||||
const response = await axios.get<{ data: Coach[] }>("/api/coaches/")
|
||||
return response.data.data
|
||||
},
|
||||
})
|
||||
|
||||
interface Coach {
|
||||
id: string
|
||||
imageUrl: string
|
||||
name: string
|
||||
title: string
|
||||
slug: string
|
||||
if (isLoading) {
|
||||
return <Loading loading />
|
||||
}
|
||||
|
||||
const defaultQuery: Query = {
|
||||
fideRating: [FIDE_RATING_MIN, FIDE_RATING_MAX],
|
||||
if (isError) {
|
||||
return (
|
||||
<FallbackMessage
|
||||
title="Unexpected Error"
|
||||
body="We're looking into this. Please refresh or try again later."
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function Search() {
|
||||
const [query, setQuery] = React.useState<Query>(defaultQuery)
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const [coaches, setCoaches] = React.useState<Coach[]>([])
|
||||
|
||||
return (
|
||||
<Container className="pt-8">
|
||||
<FilterScroll query={query} onEnable={setQuery} onModal={() => {}} />
|
||||
<Loading
|
||||
className={loading || coaches.length === 0 ? "mt-40" : "mt-10"}
|
||||
loading={loading}
|
||||
>
|
||||
{coaches.length > 0 ? (
|
||||
<FadeInStagger
|
||||
className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
|
||||
faster
|
||||
>
|
||||
{coaches.map((coach, index) => (
|
||||
{data?.map((coach, index) => (
|
||||
<FadeIn key={index} className="flex cursor-pointer flex-col">
|
||||
<CaptionImage
|
||||
title={coach.name}
|
||||
subtitle={coach.title || undefined}
|
||||
src={coach.imageUrl}
|
||||
<SearchResult
|
||||
src={coach.image_url ?? ""}
|
||||
title={coach.name ?? ""}
|
||||
subtitle={coach.name ?? ""}
|
||||
/>
|
||||
</FadeIn>
|
||||
))}
|
||||
</FadeInStagger>
|
||||
) : (
|
||||
<FallbackMessage
|
||||
title="Coming Soon"
|
||||
body="Full search functionality will be added soon! Please come back later."
|
||||
)
|
||||
}
|
||||
|
||||
export function Search() {
|
||||
const [searchParams, setSearchParams] = React.useState(defaultSearchParams)
|
||||
|
||||
return (
|
||||
<Container className="pt-8">
|
||||
<FilterScroll
|
||||
params={searchParams}
|
||||
onSelect={setSearchParams}
|
||||
onModal={() => {}}
|
||||
/>
|
||||
)}
|
||||
</Loading>
|
||||
<SearchResults />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export type Coach = {
|
||||
site: string
|
||||
username: string
|
||||
name: string | null
|
||||
image_url: string | null
|
||||
rapid: number | null
|
||||
blitz: number | null
|
||||
bullet: number | null
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export type Query = {
|
||||
fideRating: [number, number]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
export type SearchParams = {
|
||||
fideRating: [number, number]
|
||||
}
|
||||
|
||||
const FIDE_RATING_MIN = 1500
|
||||
const FIDE_RATING_MAX = 3200
|
||||
|
||||
export const defaultSearchParams: SearchParams = {
|
||||
fideRating: [FIDE_RATING_MIN, FIDE_RATING_MAX],
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@mui/base": "^5.0.0-beta.25",
|
||||
"@tanstack/react-query": "^5.12.2",
|
||||
"axios": "^1.6.2",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.16.12",
|
||||
"react": "^18.2.0",
|
||||
|
@ -192,6 +194,30 @@
|
|||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.12.1.tgz",
|
||||
"integrity": "sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.12.2.tgz",
|
||||
"integrity": "sha512-BeWZu8zVFH20oRc+S/K9ADPgWjEzP/XQCGBNz5IbApUwPQAdwkQYbXODVL5AyAlWiSxhx+P2xlARPBApj2Yrog==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.12.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/history": {
|
||||
"version": "4.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
|
||||
|
@ -250,6 +276,21 @@
|
|||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||
|
@ -258,12 +299,63 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "10.16.12",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.12.tgz",
|
||||
|
@ -303,6 +395,25 @@
|
|||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
|
@ -326,6 +437,11 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
|
@ -498,6 +614,19 @@
|
|||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.1.tgz",
|
||||
"integrity": "sha512-so+DHzZKsoOcoXrILB4rqDkMDy7NLMErRdOxvzvOKb507YINKUP4Di+shbTZDhSE/pBZ+vr7XGIpcOO0VLSA+Q=="
|
||||
},
|
||||
"@tanstack/query-core": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.12.1.tgz",
|
||||
"integrity": "sha512-WbZztNmKq0t6QjdNmHzezbi/uifYo9j6e2GLJkodsYaYUlzMbAp91RDyeHkIZrm7EfO4wa6Sm5sxJZm5SPlh6w=="
|
||||
},
|
||||
"@tanstack/react-query": {
|
||||
"version": "5.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.12.2.tgz",
|
||||
"integrity": "sha512-BeWZu8zVFH20oRc+S/K9ADPgWjEzP/XQCGBNz5IbApUwPQAdwkQYbXODVL5AyAlWiSxhx+P2xlARPBApj2Yrog==",
|
||||
"requires": {
|
||||
"@tanstack/query-core": "5.12.1"
|
||||
}
|
||||
},
|
||||
"@types/history": {
|
||||
"version": "4.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
|
||||
|
@ -556,17 +685,60 @@
|
|||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
|
||||
"devOptional": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q=="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"framer-motion": {
|
||||
"version": "10.16.12",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.12.tgz",
|
||||
|
@ -589,6 +761,19 @@
|
|||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
|
@ -611,6 +796,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@mui/base": "^5.0.0-beta.25",
|
||||
"@tanstack/react-query": "^5.12.2",
|
||||
"axios": "^1.6.2",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.16.12",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
defmodule BoardWise.Coach do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "coaches" do
|
||||
field :blitz, :integer
|
||||
field :bullet, :integer
|
||||
field :rapid, :integer
|
||||
field :site, :string
|
||||
field :username, :string
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(coach, attrs) do
|
||||
coach
|
||||
|> cast(attrs, [:site, :username, :rapid, :blitz, :bullet])
|
||||
|> validate_required([:site, :username, :rapid, :blitz, :bullet])
|
||||
end
|
||||
end
|
|
@ -20,7 +20,10 @@ defmodule BoardWise.Coaches do
|
|||
|
||||
"""
|
||||
def list_coaches do
|
||||
Repo.all(Coach, prefix: @prefix)
|
||||
Coach
|
||||
|> limit(6)
|
||||
|> where(site: "lichess")
|
||||
|> Repo.all(prefix: @prefix)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -15,18 +15,27 @@ defmodule BoardWise.Coaches.Coach do
|
|||
import Ecto.Changeset
|
||||
|
||||
schema "export" do
|
||||
field :site, :string
|
||||
field :username, :string
|
||||
field :name, :string
|
||||
field :image_url, :string
|
||||
field :blitz, :integer
|
||||
field :bullet, :integer
|
||||
field :rapid, :integer
|
||||
|
||||
field :site, :string
|
||||
field :username, :string
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(coach, attrs) do
|
||||
coach
|
||||
|> cast(attrs, [:rapid, :blitz, :bullet, :site, :username])
|
||||
|> cast(attrs, [
|
||||
:site,
|
||||
:username,
|
||||
:name,
|
||||
:image_url,
|
||||
:rapid,
|
||||
:blitz,
|
||||
:bullet
|
||||
])
|
||||
|> validate_required([:site, :username])
|
||||
|> unique_constraint(:site_username_unique, name: :site_username_unique)
|
||||
end
|
||||
|
|
|
@ -12,6 +12,8 @@ defmodule BoardWiseWeb.CoachJSON do
|
|||
%{
|
||||
site: coach.site,
|
||||
username: coach.username,
|
||||
name: coach.name,
|
||||
image_url: coach.image_url,
|
||||
rapid: coach.rapid,
|
||||
blitz: coach.blitz,
|
||||
bullet: coach.bullet
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
defmodule BoardWise.Repo.Migrations.NameImageUrl do
|
||||
use Ecto.Migration
|
||||
|
||||
@prefix "coach_scraper"
|
||||
|
||||
def change do
|
||||
alter table(:export, prefix: @prefix) do
|
||||
add :name, :string
|
||||
add :image_url, :string
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue