Add filters on source site.
parent
75e915e71c
commit
c54f832935
|
@ -10,6 +10,7 @@ import { Modal } from "./Modal"
|
||||||
import { Mode, getModeName } from "../types/Mode"
|
import { Mode, getModeName } from "../types/Mode"
|
||||||
import { SelectLanguage, SelectLanguageProps } from "./SelectLanguage"
|
import { SelectLanguage, SelectLanguageProps } from "./SelectLanguage"
|
||||||
import { SelectTitle, SelectTitleProps } from "./SelectTitle"
|
import { SelectTitle, SelectTitleProps } from "./SelectTitle"
|
||||||
|
import { Site, getSiteName } from "../types/Site"
|
||||||
import { Slider } from "./Slider"
|
import { Slider } from "./Slider"
|
||||||
import { Title } from "../types/Title"
|
import { Title } from "../types/Title"
|
||||||
import {
|
import {
|
||||||
|
@ -77,6 +78,10 @@ export function FilterModal({
|
||||||
|
|
||||||
// Registration
|
// Registration
|
||||||
|
|
||||||
|
const registerSites = register("sites", {
|
||||||
|
required: "Please select at least one site.",
|
||||||
|
})
|
||||||
|
|
||||||
const proxyLanguages = register("languages")
|
const proxyLanguages = register("languages")
|
||||||
const registerLanguages: Pick<
|
const registerLanguages: Pick<
|
||||||
SelectLanguageProps,
|
SelectLanguageProps,
|
||||||
|
@ -126,6 +131,21 @@ export function FilterModal({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-12">
|
<div className="flex flex-col gap-12">
|
||||||
|
<FieldSet error={errors?.sites?.message}>
|
||||||
|
<Label htmlFor={`${idPrefix}-rating`}>Sites:</Label>
|
||||||
|
<p className="py-2 text-sm">
|
||||||
|
Prioritize coaches from the selected site(s).
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 pt-2 text-sm">
|
||||||
|
{(Object.values(Site) as Site[]).map((s) => (
|
||||||
|
<div key={s} className="col-span-1 flex items-center gap-x-2">
|
||||||
|
<CheckBox value={s} {...registerSites} />
|
||||||
|
<div>{getSiteName(s)}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</FieldSet>
|
||||||
|
|
||||||
<Field>
|
<Field>
|
||||||
<Label htmlFor={`${idPrefix}-languages`}>
|
<Label htmlFor={`${idPrefix}-languages`}>
|
||||||
Preferred Language(s):
|
Preferred Language(s):
|
||||||
|
@ -162,6 +182,22 @@ export function FilterModal({
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<FieldSet error={errors?.modes?.message}>
|
||||||
|
<Label htmlFor={`${idPrefix}-rating`}>Mode:</Label>
|
||||||
|
<p className="py-2 text-sm">
|
||||||
|
Prefer a specific game mode? We{"'"}ll prioritize coaches that
|
||||||
|
specialize in the modes selected.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 pt-2 text-sm">
|
||||||
|
{(Object.values(Mode) as Mode[]).map((m) => (
|
||||||
|
<div key={m} className="col-span-1 flex items-center gap-x-2">
|
||||||
|
<CheckBox value={m} {...registerModes} />
|
||||||
|
<div>{getModeName(m)}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</FieldSet>
|
||||||
|
|
||||||
<Field>
|
<Field>
|
||||||
<Label htmlFor={`${idPrefix}-rating`}>Rating:</Label>
|
<Label htmlFor={`${idPrefix}-rating`}>Rating:</Label>
|
||||||
<p className="py-2 text-sm">
|
<p className="py-2 text-sm">
|
||||||
|
@ -208,22 +244,6 @@ export function FilterModal({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<FieldSet error={errors?.modes?.message}>
|
|
||||||
<Label htmlFor={`${idPrefix}-rating`}>Mode:</Label>
|
|
||||||
<p className="py-2 text-sm">
|
|
||||||
Prefer a specific game mode? We{"'"}ll prioritize coaches that
|
|
||||||
specialize in the modes selected.
|
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-3 pt-2 text-sm">
|
|
||||||
{(Object.keys(Mode) as Mode[]).map((m) => (
|
|
||||||
<div key={m} className="col-span-1 flex items-center gap-x-2">
|
|
||||||
<CheckBox value={m} {...registerModes} />
|
|
||||||
<div>{getModeName(m)}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</FieldSet>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,13 +6,16 @@ import type { SearchParams } from "../types/SearchParams"
|
||||||
import BulletIcon from "../icons/Bullet"
|
import BulletIcon from "../icons/Bullet"
|
||||||
import EnglishIcon from "../icons/English"
|
import EnglishIcon from "../icons/English"
|
||||||
import FilterIcon from "../icons/Filter"
|
import FilterIcon from "../icons/Filter"
|
||||||
|
import KnightIcon from "../icons/Knight"
|
||||||
import LightningIcon from "../icons/Lightning"
|
import LightningIcon from "../icons/Lightning"
|
||||||
|
import PawnIcon from "../icons/Pawn"
|
||||||
import RabbitIcon from "../icons/Rabbit"
|
import RabbitIcon from "../icons/Rabbit"
|
||||||
import RightArrowIcon from "../icons/RightArrow"
|
import RightArrowIcon from "../icons/RightArrow"
|
||||||
import RisingGraphIcon from "../icons/RisingGraph"
|
import RisingGraphIcon from "../icons/RisingGraph"
|
||||||
import TrophyIcon from "../icons/Trophy"
|
import TrophyIcon from "../icons/Trophy"
|
||||||
import { Button } from "./Button"
|
import { Button } from "./Button"
|
||||||
import { Mode } from "../types/Mode"
|
import { Mode } from "../types/Mode"
|
||||||
|
import { Site } from "../types/Site"
|
||||||
import { Title } from "../types/Title"
|
import { Title } from "../types/Title"
|
||||||
|
|
||||||
interface FilterOption {
|
interface FilterOption {
|
||||||
|
@ -75,11 +78,29 @@ const filters: FilterOption[] = [
|
||||||
},
|
},
|
||||||
isEnabled: (p) => p.modes.length === 1 && p.modes.includes(Mode.BULLET),
|
isEnabled: (p) => p.modes.length === 1 && p.modes.includes(Mode.BULLET),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "On Chess.com",
|
||||||
|
Icon: PawnIcon,
|
||||||
|
enable: (p) => {
|
||||||
|
p.sites.push(Site.CHESSCOM)
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
isEnabled: (p) => p.sites.includes(Site.CHESSCOM),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "On Lichess",
|
||||||
|
Icon: KnightIcon,
|
||||||
|
enable: (p) => {
|
||||||
|
p.sites.push(Site.LICHESS)
|
||||||
|
return p
|
||||||
|
},
|
||||||
|
isEnabled: (p) => p.sites.includes(Site.LICHESS),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Titled Player",
|
title: "Titled Player",
|
||||||
Icon: TrophyIcon,
|
Icon: TrophyIcon,
|
||||||
enable: (p) => {
|
enable: (p) => {
|
||||||
p.titles = Object.keys(Title)
|
p.titles = Object.keys(Title) as Title[]
|
||||||
return p
|
return p
|
||||||
},
|
},
|
||||||
isEnabled: (p) => p.titles.length > 0,
|
isEnabled: (p) => p.titles.length > 0,
|
||||||
|
|
|
@ -1,41 +1,77 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
|
||||||
type SearchResultProps = {
|
import type { Coach } from "../types/Coach"
|
||||||
title?: string
|
|
||||||
subtitle?: string
|
import PawnIcon from "../icons/Pawn"
|
||||||
src?: string
|
import KnightIcon from "../icons/Knight"
|
||||||
} & React.ComponentPropsWithoutRef<"a">
|
|
||||||
|
function getSiteIcon(coach: Coach) {
|
||||||
|
switch (coach.site) {
|
||||||
|
case "chesscom":
|
||||||
|
return ({ className, ...props }: { className?: string }) => (
|
||||||
|
<PawnIcon
|
||||||
|
className={clsx("stroke-black fill-lime-600", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case "lichess":
|
||||||
|
return ({ className, ...props }: { className?: string }) => (
|
||||||
|
<KnightIcon
|
||||||
|
className={clsx("stroke-black fill-white", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProfileUrl(coach: Coach) {
|
||||||
|
switch (coach.site) {
|
||||||
|
case "chesscom":
|
||||||
|
return `https://www.chess.com/member/${coach.username}`
|
||||||
|
case "lichess":
|
||||||
|
return `https://lichess.org/coach/${coach.username}`
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SearchResult({ coach }: { coach: Coach }) {
|
||||||
|
const profileUrl = getProfileUrl(coach)
|
||||||
|
const Component = profileUrl ? "a" : "div"
|
||||||
|
const Icon = getSiteIcon(coach)
|
||||||
|
|
||||||
export function SearchResult({
|
|
||||||
title,
|
|
||||||
subtitle,
|
|
||||||
src,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: SearchResultProps) {
|
|
||||||
return (
|
return (
|
||||||
<a
|
<Component
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"group relative h-96 overflow-hidden rounded-3xl bg-neutral-100",
|
"group relative h-96 overflow-hidden rounded-3xl bg-neutral-100",
|
||||||
className
|
{ "cursor-pointer": profileUrl }
|
||||||
)}
|
)}
|
||||||
{...props}
|
href={profileUrl || undefined}
|
||||||
|
target={profileUrl ? "_blank" : undefined}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={coach.image_url ?? ""}
|
||||||
className="h-full w-full object-cover transition duration-500 motion-safe:group-hover:scale-105"
|
className="h-full w-full object-cover transition duration-500 motion-safe:group-hover:scale-105"
|
||||||
/>
|
/>
|
||||||
|
{Icon && (
|
||||||
|
<div className="absolute -top-10 -right-10 pt-4 pr-4 flex flex-col justify-end items-start bg-radial-gradient/gray w-[5.2rem] h-[5.2rem]">
|
||||||
|
<Icon className="w-6 h-6 ml-2 mb-2" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black to-black/0 to-40% p-6">
|
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black to-black/0 to-40% p-6">
|
||||||
{title ? (
|
{coach.name ? (
|
||||||
<p className="font-display text-base/6 font-semibold tracking-wide text-white">
|
<p className="font-display text-base/6 font-semibold tracking-wide text-white">
|
||||||
{title}
|
{coach.title ? (
|
||||||
|
<span className="font-bold pr-1">{coach.title}</span>
|
||||||
|
) : null}
|
||||||
|
{coach.name}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{subtitle ? (
|
<p className="mt-2 text-sm text-white">{coach.username}</p>
|
||||||
<p className="mt-2 text-sm text-white">{subtitle}</p>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Component>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
const SvgComponent = ({ ...props }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlSpace="preserve"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path d="m7.2 16 1.1-.2c1.6-.3 3.3-.5 5-.7-2.4 2.3-3.9 5.3-4.7 7.9h14.7c.4-1.5 1.1-3 2.3-4.1l.2-.2c.2-.2.3-.4.3-.6.5-5.1-1.9-10.1-6.3-12.8-.8-1.4-2-2.4-3.6-2.9l-.9-.3c-.3-.1-.6-.1-.9.1-.2.2-.4.5-.4.8v2.4l-1.4.7c-.4.2-.6.5-.6.9v.5l-4.7 3.1c-.8.5-1.3 1.5-1.3 2.5V15c0 .3.1.6.4.8.2.2.5.2.8.2zM6.8 25c-.5.5-.8 1.2-.8 2v2c0 .6.4 1 1 1h18c.6 0 1-.4 1-1v-2c0-.8-.3-1.5-.8-2H6.8z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default SvgComponent
|
|
@ -0,0 +1,9 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
const SvgComponent = ({ ...props }) => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" {...props}>
|
||||||
|
<path d="M22.777 23.384c-.147-.983-.91-1.857-2.058-2.507-3.059-1.95-3.595-5.268-2.184-7.45 1.799-.518 3.028-1.562 3.028-2.766 0-1.095-1.017-2.058-2.555-2.613a4.336 4.336 0 0 0 1.277-3.079c0-2.396-1.933-4.338-4.318-4.338s-4.318 1.942-4.318 4.338c0 1.204.488 2.292 1.276 3.078-1.538.555-2.556 1.518-2.556 2.613 0 1.218 1.259 2.273 3.093 2.784 1.434 2.175.824 5.451-2.332 7.463-1.107.646-1.834 1.513-1.975 2.477-1.989.842-3.235 2.047-3.235 3.386 0 2.544 4.498 4.607 10.047 4.607s10.047-2.062 10.047-4.607c0-1.339-1.247-2.545-3.237-3.387z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default SvgComponent
|
|
@ -60,21 +60,9 @@ function SearchResults({ searchParams }: { searchParams: SearchParams }) {
|
||||||
{group.map((coach) => (
|
{group.map((coach) => (
|
||||||
<FadeIn
|
<FadeIn
|
||||||
key={`${coach.site}-${coach.username}`}
|
key={`${coach.site}-${coach.username}`}
|
||||||
className="flex cursor-pointer flex-col"
|
className="flex flex-col"
|
||||||
>
|
>
|
||||||
<SearchResult
|
<SearchResult coach={coach} />
|
||||||
src={coach.image_url ?? ""}
|
|
||||||
title={coach.name ?? ""}
|
|
||||||
subtitle={coach.title ?? ""}
|
|
||||||
target="_blank"
|
|
||||||
href={
|
|
||||||
coach.site === "lichess"
|
|
||||||
? `https://lichess.org/coach/${coach.username}`
|
|
||||||
: coach.site === "chesscom"
|
|
||||||
? `https://www.chess.com/member/${coach.username}`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
))}
|
))}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -4,6 +4,6 @@ export enum Mode {
|
||||||
BULLET = "BULLET",
|
BULLET = "BULLET",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getModeName = (m: Mode) => {
|
export const getModeName = (mode: Mode) => {
|
||||||
return m.charAt(0) + m.toLowerCase().slice(1)
|
return mode.charAt(0) + mode.toLowerCase().slice(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Mode } from "./Mode"
|
import { Mode } from "./Mode"
|
||||||
|
import { Site } from "./Site"
|
||||||
import { Title } from "./Title"
|
import { Title } from "./Title"
|
||||||
|
|
||||||
export type SearchParams = {
|
export type SearchParams = {
|
||||||
|
@ -6,6 +7,7 @@ export type SearchParams = {
|
||||||
modes: Mode[]
|
modes: Mode[]
|
||||||
languages: string[]
|
languages: string[]
|
||||||
titles: Title[]
|
titles: Title[]
|
||||||
|
sites: Site[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FIDE_RATING_MIN = 1500
|
export const FIDE_RATING_MIN = 1500
|
||||||
|
@ -16,11 +18,16 @@ export const defaultSearchParams: SearchParams = {
|
||||||
modes: [Mode.RAPID, Mode.BLITZ, Mode.BULLET],
|
modes: [Mode.RAPID, Mode.BLITZ, Mode.BULLET],
|
||||||
languages: [],
|
languages: [],
|
||||||
titles: [],
|
titles: [],
|
||||||
|
sites: [Site.CHESSCOM, Site.LICHESS],
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toQueryParams(p: SearchParams) {
|
export function toQueryParams(p: SearchParams) {
|
||||||
const queryParams: { [key: string]: any } = {}
|
const queryParams: { [key: string]: any } = {}
|
||||||
|
|
||||||
|
if (p.sites.length > 0) {
|
||||||
|
queryParams["sites"] = p.sites.join(",")
|
||||||
|
}
|
||||||
|
|
||||||
for (const mode of p.modes) {
|
for (const mode of p.modes) {
|
||||||
queryParams[`${mode.toLowerCase()}_gte`] = p.rating[0]
|
queryParams[`${mode.toLowerCase()}_gte`] = p.rating[0]
|
||||||
queryParams[`${mode.toLowerCase()}_lte`] = p.rating[1]
|
queryParams[`${mode.toLowerCase()}_lte`] = p.rating[1]
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
export enum Site {
|
||||||
|
CHESSCOM = "chesscom",
|
||||||
|
LICHESS = "lichess",
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSiteName(site: Site) {
|
||||||
|
switch (site) {
|
||||||
|
case Site.CHESSCOM:
|
||||||
|
return "Chess.com"
|
||||||
|
case Site.LICHESS:
|
||||||
|
return "Lichess"
|
||||||
|
default:
|
||||||
|
const _exhaustivenessCheck: never = site
|
||||||
|
return _exhaustivenessCheck
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,8 +28,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
backgroundImage: {
|
backgroundImage: {
|
||||||
"radial-gradient/black":
|
"radial-gradient/gray":
|
||||||
"radial-gradient(circle at center, black 0%, transparent 50%)",
|
"radial-gradient(circle at center, #d3d3d3 0%, transparent 100%)",
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
"4xl": "2.5rem",
|
"4xl": "2.5rem",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// https://esbuild.github.io/content-types/#tsconfig-json
|
// https://esbuild.github.io/content-types/#tsconfig-json
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// Keep in mind that ES6+ syntax to ES5 is not supported in esbuild yet.
|
// Keep in mind that ES6+ syntax to ES5 is not supported in esbuild yet.
|
||||||
"target": "es2016",
|
"target": "es2017",
|
||||||
// https://www.typescriptlang.org/docs/handbook/modules/theory.html
|
// https://www.typescriptlang.org/docs/handbook/modules/theory.html
|
||||||
"module": "nodenext",
|
"module": "nodenext",
|
||||||
// Even when transpiling a single module, the TypeScript compiler actually
|
// Even when transpiling a single module, the TypeScript compiler actually
|
||||||
|
|
|
@ -50,6 +50,7 @@ defmodule BoardWise.Coaches do
|
||||||
:bullet_lte => bullet_lte,
|
:bullet_lte => bullet_lte,
|
||||||
:languages => languages,
|
:languages => languages,
|
||||||
:titles => titles,
|
:titles => titles,
|
||||||
|
:sites => sites,
|
||||||
:page_no => page_no,
|
:page_no => page_no,
|
||||||
:page_size => page_size
|
:page_size => page_size
|
||||||
}) do
|
}) do
|
||||||
|
@ -67,17 +68,20 @@ defmodule BoardWise.Coaches do
|
||||||
? +
|
? +
|
||||||
? +
|
? +
|
||||||
(5 * (SELECT COUNT(*) FROM UNNEST(?) WHERE UNNEST = ANY(?))) +
|
(5 * (SELECT COUNT(*) FROM UNNEST(?) WHERE UNNEST = ANY(?))) +
|
||||||
CASE WHEN ? = ANY(?) THEN 5 ELSE 0 END
|
CASE WHEN ? = ANY(?) THEN 5 ELSE 0 END +
|
||||||
|
CASE WHEN ? = ANY(?) THEN 30 ELSE 0 END
|
||||||
""",
|
""",
|
||||||
c.name,
|
c.name,
|
||||||
c.image_url,
|
c.image_url,
|
||||||
rating_fragment(c.rapid, ^rapid_gte, ^rapid_lte),
|
rating_fragment(c.rapid, ^rapid_gte, ^rapid_lte),
|
||||||
rating_fragment(c.blitz, ^blitz_gte, ^blitz_lte),
|
rating_fragment(c.blitz, ^blitz_gte, ^blitz_lte),
|
||||||
rating_fragment(c.bullet, ^bullet_gte, ^bullet_lte),
|
rating_fragment(c.bullet, ^bullet_gte, ^bullet_lte),
|
||||||
type(^languages, {:array, :string}),
|
|
||||||
c.languages,
|
c.languages,
|
||||||
|
type(^languages, {:array, :string}),
|
||||||
c.title,
|
c.title,
|
||||||
type(^titles, {:array, :string})
|
type(^titles, {:array, :string}),
|
||||||
|
c.site,
|
||||||
|
type(^sites, {:array, :string})
|
||||||
)
|
)
|
||||||
|> selected_as(:score)
|
|> selected_as(:score)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule BoardWise.Coaches.QueryParams do
|
||||||
:bullet_lte,
|
:bullet_lte,
|
||||||
:titles,
|
:titles,
|
||||||
:languages,
|
:languages,
|
||||||
|
:sites,
|
||||||
page_no: 1,
|
page_no: 1,
|
||||||
page_size: 15
|
page_size: 15
|
||||||
]
|
]
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule BoardWiseWeb.CoachController do
|
||||||
|> override_param(:bullet_lte, params, :integer)
|
|> override_param(:bullet_lte, params, :integer)
|
||||||
|> override_param(:languages, params, :strlist)
|
|> override_param(:languages, params, :strlist)
|
||||||
|> override_param(:titles, params, :strlist)
|
|> override_param(:titles, params, :strlist)
|
||||||
|
|> override_param(:sites, params, :strlist)
|
||||||
|> override_param(:page_no, params, :integer)
|
|> override_param(:page_no, params, :integer)
|
||||||
|> override_param(:page_size, params, :integer)
|
|> override_param(:page_size, params, :integer)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue