diff --git a/public/css/dark.scss b/public/css/dark.scss index 7f9cb5e6f8..b4c72adfbd 100644 --- a/public/css/dark.scss +++ b/public/css/dark.scss @@ -46,6 +46,16 @@ $text-muted: $gray-400; color: $primary; } +.check-item-default { + color: $gray-600; + background: $gray-200; +} +.check-item-checked { + background: $primary; + color: $black; + border-color: $primary; +} + .text-danger { color: lighten($red, 10) !important; } diff --git a/public/css/light.scss b/public/css/light.scss index cb91e5d8fc..af5e8c4416 100644 --- a/public/css/light.scss +++ b/public/css/light.scss @@ -666,6 +666,24 @@ tr.table-warning:hover > td { color: $gray-700 !important; } +.toggle-item-default { + color: $gray-600; + background: $gray-200; + padding: 3px 7px; + border-radius: 50px; + font-size: 12px; + border-width: 1px; + cursor: pointer; + width: 40px; + display: inline-block; + margin: 0 5px 0 0; +} +.toggle-item-checked { + @extend .toggle-item-default; + background: $primary; + color: $white; + border-color: $primary; +} .navbar-border { border-bottom: 1px solid $gray-200; } diff --git a/src/common/constants.basketball.ts b/src/common/constants.basketball.ts index 2d9627d254..2f89f71d12 100644 --- a/src/common/constants.basketball.ts +++ b/src/common/constants.basketball.ts @@ -1,4 +1,4 @@ -import type { CompositeWeights, Conf, Div } from "./types"; +import type { CompositeWeights, Conf, Div, Skill } from "./types"; import type { RatingKey } from "./types.basketball"; const COMPOSITE_WEIGHTS: CompositeWeights = { @@ -10,6 +10,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1.5, 1, 1, 1, 0.5, 0.5, 0.5, 0.5], skill: { label: "V", + description: "Volume Scorer", cutoff: 0.61, }, }, @@ -18,6 +19,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1], skill: { label: "B", + description: "Ball Handler", cutoff: 0.68, }, }, @@ -26,6 +28,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.4, 1, 0.5], skill: { label: "Ps", + description: "Passer", cutoff: 0.63, }, }, @@ -42,6 +45,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 0.6, 0.2, 1, 0.4], skill: { label: "Po", + description: "Post Scorer", cutoff: 0.61, }, }, @@ -54,6 +58,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.1, 1], skill: { label: "3", + description: "Three Point Shooter", cutoff: 0.59, }, }, @@ -65,6 +70,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [2, 0.1, 0.1, 2, 0.5, 0.5], skill: { label: "R", + description: "Rebounder", cutoff: 0.61, }, }, @@ -93,6 +99,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [2.5, 1, 0.5, 0.5, 2], skill: { label: "Di", + description: "Interior Defender", cutoff: 0.57, }, }, @@ -101,6 +108,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.5, 0.5, 2, 0.5, 1], skill: { label: "Dp", + description: "Perimeter Defender", cutoff: 0.61, }, }, @@ -113,6 +121,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 1, 0.75], skill: { label: "A", + description: "Athlete", cutoff: 0.63, }, }, diff --git a/src/common/constants.football.ts b/src/common/constants.football.ts index 41e70b608a..3c2ee0107a 100644 --- a/src/common/constants.football.ts +++ b/src/common/constants.football.ts @@ -7,6 +7,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 0.2], skill: { label: "Pa", + description: "Accurate Passer", }, }, passingDeep: { @@ -14,6 +15,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 0.1, 0.2], skill: { label: "Pd", + description: "Deep Passer", }, }, passingVision: { @@ -21,6 +23,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 0.5], skill: { label: "Ps", + description: "Smart Passer", }, }, athleticism: { @@ -28,6 +31,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 0.2], skill: { label: "A", + description: "Athletic", }, }, rushing: { @@ -35,6 +39,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.5, 1, 1], skill: { label: "X", + description: "Explosive Runner", }, }, catching: { @@ -42,6 +47,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.2, 1], skill: { label: "H", + description: "Hands", }, }, gettingOpen: { @@ -53,6 +59,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.5, 1, 0.2, 1], skill: { label: "Bp", + description: "Pass Blocker", }, }, runBlocking: { @@ -60,6 +67,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.5, 1, 0.4, 1], skill: { label: "Br", + description: "Run Blocker", }, }, passRushing: { @@ -67,6 +75,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 0.5, 1, 0.25], skill: { label: "PR", + description: "Pass Rusher", }, }, runStopping: { @@ -74,6 +83,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.5, 1, 0.5, 1, 1], skill: { label: "RS", + description: "Run Stopper", }, }, passCoverage: { @@ -81,6 +91,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [0.1, 1, 1, 0.25], skill: { label: "L", + description: "Lockdown Coverage", }, }, tackling: { diff --git a/src/common/constants.hockey.ts b/src/common/constants.hockey.ts index 2f2c129ef2..dcdb71ac9e 100644 --- a/src/common/constants.hockey.ts +++ b/src/common/constants.hockey.ts @@ -7,6 +7,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 1, 1, 0.25, 0.1], skill: { label: "Pm", + description: "Playmaker", cutoff: 0.65, }, }, @@ -15,6 +16,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 1, 0.25, 0.1], skill: { label: "Pw", + description: "Power", cutoff: 0.65, }, }, @@ -23,6 +25,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 1, 0.25, 0.25, 0.1], skill: { label: "G", + description: "Grinder", cutoff: 0.65, }, }, @@ -31,6 +34,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 0.25, 0.1], skill: { label: "E", + description: "Enforcer", cutoff: 0.65, }, }, @@ -39,6 +43,7 @@ const COMPOSITE_WEIGHTS: CompositeWeights = { weights: [1, 1, 0.25], skill: { label: "S", + description: "Sniper", cutoff: 0.65, }, }, diff --git a/src/common/constants.ts b/src/common/constants.ts index 28fcddb972..875df9bf4d 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -3,7 +3,13 @@ import * as constantsBasketball from "./constants.basketball"; import * as constantsFootball from "./constants.football"; import * as constantsHockey from "./constants.hockey"; -import type { CompositeWeights, Phase, DraftType, MoodTrait } from "./types"; +import type { + CompositeWeights, + Phase, + DraftType, + MoodTrait, + CompositeWeight, +} from "./types"; const ACCOUNT_API_URL = process.env.NODE_ENV === "development" @@ -142,6 +148,17 @@ const POSITIONS = bySport({ hockey: constantsHockey.POSITIONS, }); +const SKILLS: { [key: string]: string } = Object.values( + bySport({ + basketball: constantsBasketball.COMPOSITE_WEIGHTS, + football: constantsFootball.COMPOSITE_WEIGHTS, + hockey: constantsHockey.COMPOSITE_WEIGHTS, + }), +).reduce((skills: { [key: string]: string }, item: CompositeWeight) => { + if (item.skill) skills[item.skill.label] = item.skill.description; + return skills; +}, {}); + const TEAM_STATS_TABLES: { [key: string]: { name: string; @@ -285,5 +302,6 @@ export { SUBREDDIT_NAME, TEAM_STATS_TABLES, TIME_BETWEEN_GAMES, + SKILLS, TWITTER_HANDLE, }; diff --git a/src/common/types.ts b/src/common/types.ts index b7afabc02c..e058246280 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -100,15 +100,19 @@ export type AllStars = { }; }; +export type CompositeWeight = { + ratings: (RatingKey | number)[]; + weights?: number[] | undefined; + skill?: + | { + label: string; + cutoff?: number | undefined; + description: string; + } + | undefined; +}; export type CompositeWeights = { - [key: string]: { - ratings: (RatingKey | number)[]; - weights?: number[]; - skill?: { - label: string; - cutoff?: number; - }; - }; + [key: string]: CompositeWeight; }; // Not exact because https://github.com/facebook/flow/issues/2386 - same thing elsewhere diff --git a/src/ui/components/SkillsBlock.tsx b/src/ui/components/SkillsBlock.tsx index 33568a2a69..63e1dfb345 100644 --- a/src/ui/components/SkillsBlock.tsx +++ b/src/ui/components/SkillsBlock.tsx @@ -1,40 +1,8 @@ import PropTypes from "prop-types"; -import { bySport } from "../../common"; +import { SKILLS } from "../../common/constants"; -const tooltips = bySport({ - basketball: { - "3": "Three Point Shooter", - S: "Scorer", - A: "Athlete", - B: "Ball Handler", - Di: "Interior Defender", - Dp: "Perimeter Defender", - Po: "Post Scorer", - Ps: "Passer", - R: "Rebounder", - V: "Volume Scorer", - }, - football: { - Pa: "Accurate Passer", - Pd: "Deep Passer", - Ps: "Smart Passer", - A: "Athletic", - X: "Explosive Runner", - H: "Hands", - Bp: "Pass Blocker", - Br: "Run Blocker", - PR: "Pass Rusher", - RS: "Run Stopper", - L: "Lockdown Coverage", - }, - hockey: { - Pm: "Playmaker", - Pw: "Power", - G: "Grinder", - E: "Enforcer", - S: "Sniper", - }, -}); +const tooltips = SKILLS; +console.log(tooltips); const SkillsBlock = ({ className, @@ -56,7 +24,7 @@ const SkillsBlock = ({ title={ // https://github.com/microsoft/TypeScript/issues/21732 // @ts-ignore - tooltips.hasOwnProperty(skill) ? tooltips[skill] : null + tooltips.hasOwnProperty(skill) ? tooltips[skill] : undefined } > {skill} diff --git a/src/ui/components/ToggleItem.tsx b/src/ui/components/ToggleItem.tsx new file mode 100644 index 0000000000..820edebd0a --- /dev/null +++ b/src/ui/components/ToggleItem.tsx @@ -0,0 +1,46 @@ +import { useEffect, useState } from "react"; + +type Props = { + onCheck?: (chkd: boolean) => void; + text: string; + checked?: boolean; + className?: string; + tooltip?: string; +}; + +export const ToggleItem = ({ + onCheck, + text, + checked, + className, + tooltip, +}: Props) => { + const [isChecked, setChecked] = useState(false); + + const toggleCheck = () => { + const newState = !isChecked; + setChecked(newState); + if (onCheck) onCheck(newState); + }; + + useEffect(() => { + if (checked !== undefined) setChecked(checked); + }, [checked]); + + const classChecked = "toggle-item-checked text-center " + (className || ""); + const classDefault = "toggle-item-default text-center " + (className || ""); + return ( +
+ {" "} + {text}{" "} +
+ ); +}; diff --git a/src/ui/views/TradeFilter/FilterGroup.tsx b/src/ui/views/TradeFilter/FilterGroup.tsx new file mode 100644 index 0000000000..76e0844c49 --- /dev/null +++ b/src/ui/views/TradeFilter/FilterGroup.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useState } from "react"; +import { ToggleItem } from "../../components/ToggleItem"; +import type { filterType } from "./TradeFilter"; + +export default function FilterGroup(props: { + title: string; + statuses: filterType; + values: string[]; + type: string; + onUpdate: (type: string, value: string[]) => void; +}) { + const [activeFilters, setActiveFilters] = useState( + props.statuses[props.type].filterData, + ); + + useEffect(() => { + setActiveFilters(props.statuses[props.type].filterData); + }, [props.statuses]); + + useEffect(() => { + props.onUpdate(props.type, activeFilters); + }, [activeFilters]); + + const toggleFilter = (value: string, type: string) => { + //remove from filters if it already exists + if (activeFilters.includes(value)) { + setActiveFilters(prevFilters => { + const indexToRemove = prevFilters.indexOf(value); + const toreturn = [...prevFilters]; + toreturn.splice(indexToRemove, 1); + //props.onUpdate(type, toreturn) + return toreturn; + }); + } + //or add it + else { + setActiveFilters(prevFilters => { + //props.onUpdate(props.type, [...prevFilters, value]) + return [...prevFilters, value]; + }); + } + }; + + const CheckBox = (props: { + value: string; + type: string; + tooltip: string | undefined; + }) => { + return ( + toggleFilter(props.value, props.type)} + checked={activeFilters.includes(props.value)} + className="mt-2" + tooltip={props.tooltip} + /> + ); + }; + + return ( + <> + + {props.title} + + {props.values.map(val => ( + + ))} + + ); +} diff --git a/src/ui/views/TradeFilter/TradeFilter.tsx b/src/ui/views/TradeFilter/TradeFilter.tsx new file mode 100644 index 0000000000..80e8ee3b44 --- /dev/null +++ b/src/ui/views/TradeFilter/TradeFilter.tsx @@ -0,0 +1,139 @@ +import React, { useState } from "react"; +import { POSITIONS, SKILLS } from "../../../common/constants"; +import type FilterItem from "../../../worker/core/trade/FilterItem"; +import FilterGroup from "./FilterGroup"; + +interface tradeFilterProps { + setFilters: (filters: Filter) => void; + filters: Filter; +} +export type Filter = { + [key: string]: FilterItem; +}; + +//may be a more elegant way to approach this? +const POS = POSITIONS.filter( + pos => !["GF", "FC", "F", "G", "KR", "PR"].includes(pos), +); + +const SKILL = Object.keys(SKILLS); + +const TradeFilter = (props: tradeFilterProps) => { + const [filters, setFilters] = useState>(props.filters); + const [balancedOnly, setBalancedOnly] = useState(false); + const [salaryCap, setSalaryCap] = useState(""); + + const updateFilters = (type: string, value: string[]) => { + if (value !== filters[type].filterData) { + filters[type].update(value); + setFilters({ ...filters }); + props.setFilters(filters); + } + }; + + const toggleBalancedOnly = () => { + setBalancedOnly(filters.salaryCap.filterData !== "-1"); + if (filters.salaryCap.filterData !== "-1") filters.salaryCap.update("-1"); + else filters.salaryCap.update(""); + props.setFilters(filters); + }; + + const clearFilters = () => { + filters.salaryCap.update(""); + filters.pos.update([]); + filters.skill.update([]); + setFilters({ ...filters }); + setSalaryCap(""); + setBalancedOnly(false); + props.setFilters(filters); + }; + + const setFilterSalaryCap = ( + e: React.ChangeEvent, + validate = false, + ) => { + let newVal = e.target.value; + if (validate) { + const numVal = Number(e.target.value); + newVal = numVal ? numVal.toString() : ""; + } + filters.salaryCap.update(newVal); + setSalaryCap(newVal); + props.setFilters(filters); + }; + + return ( +
+
+

Filter Options

+ + clear + +
+
+ +
+
+ +
+
+ + Salary (Max) + +
+
+
$
+
+ setFilterSalaryCap(e, true)} + value={balancedOnly ? "" : salaryCap} + disabled={balancedOnly} + /> +
+
M
+
+
+
+
+ +
+
+
+
+ ); +}; + +export default TradeFilter; diff --git a/src/ui/views/TradingBlock.tsx b/src/ui/views/TradingBlock.tsx index 22d7a12c26..cb34c55dad 100644 --- a/src/ui/views/TradingBlock.tsx +++ b/src/ui/views/TradingBlock.tsx @@ -6,8 +6,12 @@ import { getCols, helpers, toWorker } from "../util"; import { DataTable, PlayerNameLabels } from "../components"; import type { View, ThenArg } from "../../common/types"; import type api from "../../worker/api"; +import TradeFilter, { Filter } from "../views/TradeFilter/TradeFilter"; +import { AnimatePresence, motion } from "framer-motion"; +import FilterItem from "../../worker/core/trade/FilterItem"; +import _ from "lodash"; -type OfferType = ThenArg>[0]; +type Offer = ThenArg>[0]; type OfferProps = { challengeNoRatings: boolean; @@ -18,7 +22,7 @@ type OfferProps = { ) => Promise; i: number; stats: string[]; -} & OfferType; +} & Offer; const Offer = (props: OfferProps) => { const { @@ -173,19 +177,65 @@ const pickCols = getCols("", "Draft Picks"); pickCols[0].sortSequence = []; pickCols[1].width = "100%"; +const populateExtendedPositions = (items: string[]): string[] => { + let extPos = [...items].filter(pos => !["GF", "FC", "F", "G"].includes(pos)); + if (extPos.includes("PG") || extPos.includes("SG")) extPos.push("G"); + if (extPos.includes("PF") || extPos.includes("SF")) extPos.push("F"); + if (extPos.includes("PF") || extPos.includes("C")) extPos.push("FC"); + if (extPos.includes("SF") || extPos.includes("SG")) extPos.push("GF"); + return extPos; +}; + const TradingBlock = (props: View<"tradingBlock">) => { const [state, setState] = useState<{ asking: boolean; - offers: OfferType[]; + offers: Offer[]; pids: number[]; dpids: number[]; + filters: Filter; + showFilters: boolean; + filterVerbiage: string; }>({ asking: false, offers: [], pids: props.initialPid !== undefined ? [props.initialPid] : [], dpids: [], + filters: { + pos: new FilterItem([], "POS", populateExtendedPositions), + salaryCap: new FilterItem("", "SALARY_CAP"), + skill: new FilterItem([], "SKILL"), + }, + showFilters: false, + filterVerbiage: "Filter", }); + const setFilters = (filters: Filter) => { + setState(prevState => { + let count = 0; + if (filters.pos.filterData && filters.pos.filterData.length > 0) count++; + if (filters.skill.filterData && filters.skill.filterData.length > 0) + count++; + if (filters.salaryCap.filterData !== "") count++; + + const plurality = count > 1 ? "Filters" : "Filter"; + + return { + ...prevState, + filters: filters, + filterVerbiage: count > 0 ? ` (${count} ${plurality})` : plurality, + }; + }); + }; + + const toggleFilterDisplay = () => { + setState(prevState => { + return { + ...prevState, + showFilters: !prevState.showFilters, + }; + }); + }; + const beforeOffersRef = useRef(null); const handleChangeAsset = (type: "pids" | "dpids", id: number) => { @@ -215,11 +265,17 @@ const TradingBlock = (props: View<"tradingBlock">) => { offers: [], })); - const offers: OfferType[] = await toWorker( + const offers: Offer[] = await toWorker( "main", "getTradingBlockOffers", state.pids, state.dpids, + Object.values(state.filters).map((filter: FilterItem) => { + return { + filterFunction: filter.filterFunction, + filterData: filter.filterData, + }; + }), ); setState(prevState => ({ @@ -388,6 +444,18 @@ const TradingBlock = (props: View<"tradingBlock">) => {
+ + {state.showFilters === true && ( + + + + )} + +
+ + {" "} + {state.filterVerbiage} +
{state.offers.map((offer, i) => { diff --git a/src/worker/api/index.ts b/src/worker/api/index.ts index a57adc64e6..cf0ccdeba4 100644 --- a/src/worker/api/index.ts +++ b/src/worker/api/index.ts @@ -80,6 +80,9 @@ import type { ScheduleGameWithoutKey, Conf, Div, + ThenArg, + ContractInfo, + DraftPick, LocalStateUI, } from "../../common/types"; import orderBy from "lodash/orderBy"; @@ -91,6 +94,25 @@ import { } from "../core/season/awards"; import { getScore } from "../core/player/checkJerseyNumberRetirement"; import type { NewLeagueTeam } from "../../ui/views/NewLeague/types"; +import * as filter_functions from "../core/trade/filterFunctions"; + +export type AugmentedOffer = { + tid: number; + abbrev: string; + region: string; + name: string; + strategy: string; + won: number; + lost: number; + otl: number; + tied: number; + pids: number[]; + dpids: number[]; + warning: string | null | undefined; + payroll: number | ContractInfo[]; + picks: DraftPick[]; + players: Player[]; +}; const acceptContractNegotiation = async ( pid: number, @@ -1312,13 +1334,42 @@ const getRandomRatings = async (age: number, pos: string | undefined) => { }; }; -const getTradingBlockOffers = async (pids: number[], dpids: number[]) => { +const filterOffers = ( + offers: AugmentedOffer[], + filters: [{ filterFunction: string; filterData: any }], +) => { + //filtering logic + return offers.filter(offer => { + let filteredPlayers = offer.players; + const qualifies = filters.reduce((qualifies: boolean, filter) => { + const command = Object.values(filter_functions).find( + c => c.name === filter.filterFunction, + ); + if (!qualifies) return false; + else if (command && filter.filterData) { + const newRes = command.execute( + filteredPlayers, + offer, + filter.filterData, + ); + filteredPlayers = newRes.players; + + return qualifies && newRes.qualifies; + } else return true; + }, true); + return qualifies; + }); +}; + +const getTradingBlockOffers = async ( + pids: number[], + dpids: number[], + filters: [{ filterFunction: string; filterData: any }], +): Promise => { const getOffers = async (userPids: number[], userDpids: number[]) => { - // Pick 10 random teams to try (or all teams, if g.get("numActiveTeams") < 10) const teams = await idb.cache.teams.getAll(); const tids = teams.filter(t => !t.disabled).map(t => t.tid); random.shuffle(tids); - tids.splice(10); const offers: TradeTeam[] = []; for (const tid of tids) { @@ -1350,10 +1401,12 @@ const getTradingBlockOffers = async (pids: number[], dpids: number[]) => { } } - return offers; + return offers.slice(0, 10); }; - const augmentOffers = async (offers: TradeTeam[]) => { + const augmentOffers = async ( + offers: TradeTeam[], + ): Promise => { if (offers.length === 0) { return []; } @@ -1407,7 +1460,6 @@ const getTradingBlockOffers = async (pids: number[], dpids: number[]) => { tid, }); picks = picks.filter(dp => offer.dpids.includes(dp.dpid)); - const picks2 = picks.map(dp => { return { ...dp, @@ -1437,8 +1489,17 @@ const getTradingBlockOffers = async (pids: number[], dpids: number[]) => { ); }; - const offers = await getOffers(pids, dpids); - return augmentOffers(offers); + //since filters reduce the number of viable offers, try up to 10 times to get some good offers + let filteredOffers: AugmentedOffer[] = []; + let iterations = 0; + while (filteredOffers.length === 0 && iterations < 10) { + const offers = await getOffers(pids, dpids); + const augmentedOffers = await augmentOffers(offers); + filteredOffers = filterOffers(augmentedOffers, filters); + iterations++; + } + + return filteredOffers; }; const getVersionWorker = async () => { diff --git a/src/worker/core/trade/filterFunctions.ts b/src/worker/core/trade/filterFunctions.ts new file mode 100644 index 0000000000..a8b31363e1 --- /dev/null +++ b/src/worker/core/trade/filterFunctions.ts @@ -0,0 +1,73 @@ +import _ from "lodash"; +import type { AugmentedOffer } from "src/worker/api"; + +export interface FilterFunction { + name: string; + execute: (filteredPlayers: any, players: any, filterData: any) => any; +} + +export const POS: FilterFunction = { + name: "POS", + execute(filteredPlayers: any, offer: any, filterData: any) { + if (offer.players.length === 0) + return { + players: offer.players, + qualifies: true, + }; + else if (filterData && filterData.length > 0) { + const qualifyingPlayers = filteredPlayers.filter((pl: any) => { + return filterData.includes(pl.ratings.pos); + }); + return { + players: qualifyingPlayers, + qualifies: qualifyingPlayers.length > 0, + }; + } else + return { + players: filteredPlayers, + qualifies: filteredPlayers.length > 0, + }; + }, +}; + +export const SALARY_CAP: FilterFunction = { + name: "SALARY_CAP", + execute(filteredPlayers: any, offer: AugmentedOffer, filterData: any) { + if (filterData === "-1") { + return { + players: filteredPlayers, + qualifies: offer.warning ? false : true, + }; + } else if (filterData == "") + return { players: filteredPlayers, qualifies: true }; + else { + const qualifies = + offer.players.reduce( + (total: number, pl: any) => (total += pl.contract.amount), + 0, + ) <= Number(filterData); + return { players: filteredPlayers, qualifies: qualifies }; + } + }, +}; + +export const SKILL: FilterFunction = { + name: "SKILL", + execute(filteredPlayers: any, offer: any, filterData: any) { + if (offer.players.length === 0) + return { + players: offer.players, + qualifies: true, + }; + else if (filterData && filterData.length > 0) { + const qualifyingPlayers = filteredPlayers.filter( + (player: any) => + _.intersection(filterData, player.ratings.skills).length > 0, + ); + return { + players: qualifyingPlayers, + qualifies: qualifyingPlayers.length > 0, + }; + } else return { players: filteredPlayers, qualifies: true }; + }, +}; diff --git a/src/worker/core/trade/filterItem.ts b/src/worker/core/trade/filterItem.ts new file mode 100644 index 0000000000..5d96ea8d6b --- /dev/null +++ b/src/worker/core/trade/filterItem.ts @@ -0,0 +1,19 @@ +export default class FilterItem { + filterData: FilterData; //filter items selected, ex for Pos: C, PF... + filterFunction: string; //function that will decide if the offer qualifies given list of players + customUpdate: ((data: any) => any) | undefined; //used if you want to transform filter options before setting it + + constructor( + filterData: any, + filterFunction: string, + updateItems?: ((data: any) => any) | undefined, + ) { + this.filterData = filterData; + this.filterFunction = filterFunction; + this.customUpdate = updateItems; + } + + update(data: any): void { + this.filterData = this.customUpdate ? this.customUpdate(data) : data; + } +}