) {\r\n const {\r\n placeholder,\r\n data,\r\n getOptionCb,\r\n onChange,\r\n onEnterPress,\r\n value,\r\n filterOptions\r\n } = props;\r\n\r\n return (\r\n \r\n {\r\n if (onEnterPress && e.key.toLowerCase() === \"enter\") {\r\n onEnterPress();\r\n }\r\n }}\r\n onChange={(_, value) => {\r\n if (onChange) {\r\n onChange(value);\r\n }\r\n }}\r\n getOptionLabel={(option) => getOptionCb(option)}\r\n renderInput={(params) => (\r\n \r\n )}\r\n />\r\n \r\n );\r\n}\r\n\r\nexport default observer(AutoCompleteInput);\r\n","import React from \"react\";\r\nimport \"./AutoOptimizeSwitch.scss\";\r\ninterface Props {\r\n autoOptimize: string;\r\n setAutoOptimize: (value: string) => void;\r\n}\r\n\r\nconst AutoOptimizeSwitch = (props: Props) => {\r\n const { autoOptimize, setAutoOptimize } = props;\r\n return (\r\n \r\n \r\n setAutoOptimize(autoOptimize === \"balanced\" ? \"max\" : \"balanced\")\r\n }\r\n />\r\n \r\n Cost Effective\r\n \r\n\r\n \r\n setAutoOptimize(autoOptimize === \"balanced\" ? \"max\" : \"balanced\")\r\n }\r\n />\r\n \r\n Max Applicants\r\n \r\n
\r\n );\r\n};\r\nexport default AutoOptimizeSwitch;\r\n","import { Account } from \"./../../models/Account\";\r\nimport { Box, Button, FormControlLabel, Radio } from \"@material-ui/core\";\r\nimport AutoCompleteInput from \"../common/inputs/auto-complete-input/AutoCompleteInput\";\r\nimport Input from \"../common/inputs/input/Input\";\r\nimport MonetizationOnOutlinedIcon from \"@material-ui/icons/MonetizationOnOutlined\";\r\nimport PeopleAltIcon from \"@material-ui/icons/PeopleAlt\";\r\nimport Loader from \"../common/feedback/loader/Loader\";\r\nimport AutoOptimizeSwitch from \"./components/auto-optimize-switch/AutoOptimizeSwitch\";\r\n\r\nexport class InputPageBuilder {\r\n /**\r\n *\r\n */\r\n constructor(public classes: any) { }\r\n\r\n public Title(title: string) {\r\n return {title} ;\r\n }\r\n\r\n public AccountNameAutocompleteInput(\r\n readyAccounts: Account[],\r\n selectedAccount: Account | null,\r\n setSelectedAccount: (account: Account | null) => void,\r\n handleSubmit: () => void,\r\n loading: boolean\r\n ) {\r\n return (\r\n \r\n Select a client \r\n {loading ? (\r\n \r\n \r\n \r\n ) : (\r\n v?.accountName ?? \"\"}\r\n onChange={(v) => setSelectedAccount(v)}\r\n onEnterPress={handleSubmit}\r\n value={selectedAccount}\r\n />\r\n )}\r\n \r\n );\r\n }\r\n\r\n public GoalSettingSection(\r\n budget: string,\r\n setBudget: (value: string) => void,\r\n applicants: string,\r\n setApplicants: (value: string) => void,\r\n handleSubmit: () => void,\r\n goalType: string,\r\n setGoalType: (value: string) => void,\r\n autoOptimize: string,\r\n setAutoOptimize: (value: string) => void\r\n ) {\r\n const handleChange = (event: React.ChangeEvent) => {\r\n setGoalType(event.target.value);\r\n setBudget(\"\");\r\n setApplicants(\"\");\r\n setAutoOptimize(\"balanced\")\r\n };\r\n return (\r\n \r\n\r\n \r\n \r\n }\r\n label=\"Set a goal\"\r\n />\r\n \r\n }\r\n label=\"Auto optimize\"\r\n />\r\n \r\n \r\n {goalType === 'manual' ?\r\n \r\n \r\n }\r\n value={budget}\r\n // valueSetter={setBudget}\r\n disabled={!!applicants}\r\n onEnterPress={handleSubmit}\r\n onChange={(e) => setBudget(e.target.value.substring(0, 7))\r\n }\r\n type=\"number\"\r\n />\r\n \r\n OR
\r\n \r\n }\r\n value={applicants}\r\n //valueSetter={setApplicants}\r\n disabled={!!budget}\r\n onEnterPress={handleSubmit}\r\n onChange={(e) => setApplicants(e.target.value.substring(0, 7))\r\n }\r\n type=\"number\"\r\n />\r\n \r\n \r\n :\r\n \r\n \r\n \r\n }\r\n \r\n \r\n );\r\n }\r\n\r\n public ButtonsSection(\r\n loading: boolean,\r\n handleSubmit: () => void,\r\n handleClear: () => void\r\n ) {\r\n if (loading) {\r\n return (\r\n \r\n \r\n \r\n );\r\n }\r\n\r\n return (\r\n <>\r\n \r\n \r\n Generate Prediction Report\r\n \r\n \r\n\r\n \r\n \r\n Clear form\r\n \r\n \r\n >\r\n );\r\n }\r\n}\r\n","import { Box, Button, Paper } from \"@material-ui/core\";\r\nimport React, { useContext, useState, useEffect } from \"react\";\r\nimport { useStyles } from \"./InputPage.styles\";\r\nimport useOnLoadAsync from \"./../../hooks/useOnLoadAsync\";\r\nimport { Account } from \"../../models/Account\";\r\nimport { message } from \"antd\";\r\nimport {\r\n EPredictionType,\r\n UserPredictionRequest,\r\n} from \"../../models/prediction/UserPredictionRequest\";\r\nimport { RootStoreContext } from \"./../../stores/RootStoreContext\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { withRouter } from \"react-router\";\r\nimport { RouteComponentProps, useLocation } from \"react-router-dom\";\r\nimport Loader from \"../common/feedback/loader/Loader\";\r\nimport { Routes } from \"../../constants/Routes\";\r\nimport useSetCurrentPage from \"../../hooks/useSetCurrentPage\";\r\nimport { InputPageBuilder } from \"./InputPage.builder\";\r\nimport useKeyboardListener from \"./../../hooks/useKeyboardListener\";\r\nimport { Guid } from \"../../services/Guid\";\r\nimport { useSnackbar } from \"notistack\";\r\n\r\ninterface Props extends RouteComponentProps { }\r\n\r\nconst InputPage = (props: Props) => {\r\n const { history } = props;\r\n\r\n const { predictionStore, uiStore } = useContext(RootStoreContext);\r\n const { enqueueSnackbar, closeSnackbar } = useSnackbar();\r\n const classes = useStyles();\r\n const [readyAccounts, setReadyAccounts] = useState([]);\r\n const [loading, setLoading] = useState(true);\r\n\r\n const calculateInitialState = (forType: \"budget\" | \"applicants\") => {\r\n if (\r\n !predictionStore.userPredictionRequest ||\r\n predictionStore.userPredictionRequest.amount === 0\r\n )\r\n return \"\";\r\n\r\n if (\r\n forType === \"budget\" &&\r\n predictionStore.userPredictionRequest.predictionType ===\r\n EPredictionType.Budget\r\n ) {\r\n return predictionStore.userPredictionRequest.amount.toString();\r\n }\r\n if (\r\n forType === \"applicants\" &&\r\n predictionStore.userPredictionRequest.predictionType ===\r\n EPredictionType.Applicants\r\n ) {\r\n return predictionStore.userPredictionRequest.amount.toString();\r\n }\r\n\r\n return \"\";\r\n };\r\n\r\n // form\r\n const [budget, setBudget] = useState(\r\n calculateInitialState(\"budget\")\r\n );\r\n const [applicants, setApplicants] = useState(\r\n calculateInitialState(\"applicants\")\r\n );\r\n const [selectedAccount, setSelectedAccount] = useState(\r\n predictionStore.account || null\r\n );\r\n const [goalType, setGoalType] = useState(\r\n predictionStore.goalType\r\n );\r\n const [autoOptimize, setAutoOptimize] = useState(\r\n predictionStore.autoOptimize\r\n );\r\n const [loader, setLoader] = useState(false);\r\n\r\n const handleSubmit = async () => {\r\n if (!selectedAccount) {\r\n message.error(\"Please select an account to make a prediction for\");\r\n return;\r\n }\r\n setLoader(true);\r\n // enqueueSnackbar(\"Generating your predictions\", {\r\n // variant: \"info\"\r\n // });\r\n\r\n let predictionType = EPredictionType.NoRequest;\r\n let goalText = null;\r\n if (budget || applicants) {\r\n predictionType = budget\r\n ? EPredictionType.Budget\r\n : EPredictionType.Applicants;\r\n }\r\n else{\r\n goalText = \"Cost Effective\";\r\n }\r\n\r\n let amount: string = budget || applicants || \"0\";\r\n \r\n if (goalType == \"autoOptimize\") {\r\n if (autoOptimize == \"max\") {\r\n amount = \"9999999\";\r\n goalText = \"Max Applicants\";\r\n predictionType = EPredictionType.Budget;\r\n }\r\n else {\r\n amount = \"0\";\r\n goalText = \"Cost Effective\";\r\n predictionType = EPredictionType.NoRequest;\r\n }\r\n }\r\n\r\n const userPredictionRequest = new UserPredictionRequest(\r\n selectedAccount.accountId,\r\n predictionType,\r\n Number(amount),\r\n null\r\n );\r\n try {\r\n const _ = await predictionStore.getPredictionsAndBuildState(\r\n userPredictionRequest,\r\n selectedAccount,\r\n goalType,\r\n autoOptimize,\r\n goalText\r\n );\r\n history.push(Routes.PredictionPage);\r\n } catch (error) {\r\n message.error(error.message);\r\n } finally {\r\n setLoader(false);\r\n }\r\n };\r\n\r\n const handleClear = () => {\r\n setApplicants(\"\");\r\n setBudget(\"\");\r\n setSelectedAccount(null);\r\n setGoalType(\"manual\");\r\n setAutoOptimize(\"balanced\");\r\n };\r\n\r\n /* Hooks\r\n * ################################################\r\n */\r\n\r\n useOnLoadAsync(async () => {\r\n const accounts = await predictionStore.GetListOfReadyAccounts();\r\n setReadyAccounts(accounts);\r\n setLoading(false);\r\n });\r\n\r\n useSetCurrentPage();\r\n\r\n useKeyboardListener(Guid.NewGuid(), [\"alt\", \"2\"], () => {\r\n history.push(Routes.AdminPreload);\r\n });\r\n\r\n if (uiStore.applicationLoader) {\r\n return (\r\n \r\n \r\n \r\n );\r\n }\r\n\r\n const builder = new InputPageBuilder(classes);\r\n return (\r\n \r\n \r\n \r\n \r\n {builder.Title(\r\n \"Campaign Simulator\"\r\n )}\r\n\r\n {builder.AccountNameAutocompleteInput(\r\n readyAccounts,\r\n selectedAccount,\r\n setSelectedAccount,\r\n handleSubmit,\r\n loading\r\n )}\r\n\r\n {builder.GoalSettingSection(\r\n budget,\r\n setBudget,\r\n applicants,\r\n setApplicants,\r\n handleSubmit,\r\n goalType,\r\n setGoalType,\r\n autoOptimize,\r\n setAutoOptimize\r\n )}\r\n\r\n {builder.ButtonsSection(\r\n loader,\r\n handleSubmit,\r\n handleClear\r\n )}\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default withRouter(observer(InputPage));\r\n","import { Box } from \"@material-ui/core\";\r\nimport React from \"react\";\r\nimport \"./HorizontalDivider.scss\";\r\n\r\ninterface Props {\r\n text?: string;\r\n noteText?: string;\r\n}\r\n\r\nconst HorizontalDivider = (props: Props) => {\r\n const { text, noteText } = props;\r\n\r\n return (\r\n \r\n {text && (\r\n \r\n {text}\r\n \r\n )}\r\n {noteText && (\r\n \r\n {noteText}\r\n \r\n )}\r\n \r\n \r\n );\r\n};\r\n\r\nexport default HorizontalDivider;\r\n","import { Box, Paper } from \"@material-ui/core\";\r\nimport React from \"react\";\r\nimport { NumbersHelper } from \"../../../../services/helpers/NumbersHelper\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport HelpIcon from \"@material-ui/icons/Help\";\r\nimport \"./InformationBox.scss\";\r\nimport { Tooltip } from \"antd\";\r\n\r\nexport interface DataModel {\r\n name: string;\r\n value: string | number;\r\n prefix?: string;\r\n postfix?: string;\r\n explanation?: string;\r\n}\r\n\r\ninterface Props {\r\n title: string;\r\n data?: DataModel[];\r\n style?: React.CSSProperties;\r\n}\r\n\r\nconst InformationBox = (props: Props) => {\r\n const { title, data, style } = props;\r\n\r\n const renderData = () => {\r\n if (data) {\r\n return data.map((res: DataModel, i: number) => {\r\n let finalValue = res.value;\r\n if(res.value === \"-\") {\r\n finalValue = \"-\";\r\n }\r\n else if (typeof res.value == \"number\" || typeof res.value == \"string\") {\r\n finalValue = NumbersHelper.numberWithCommas(res.value);\r\n if (res.prefix) {\r\n finalValue = `${res.prefix}${finalValue}`;\r\n }\r\n if (res.postfix) {\r\n finalValue = `${finalValue}${res.postfix}`;\r\n }\r\n }\r\n return (\r\n \r\n \r\n \r\n {res.name}\r\n \r\n {res.explanation && (\r\n \r\n \r\n \r\n \r\n \r\n )}\r\n \r\n \r\n {finalValue}\r\n \r\n \r\n );\r\n });\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n {title}\r\n \r\n\r\n \r\n {renderData()}\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default observer(InformationBox);\r\n","import { makeStyles } from \"@material-ui/core\";\r\n\r\nexport const useStyles = makeStyles((theme) => ({\r\n whiteSection: {\r\n boxShadow: \"0 1px 8px 0 rgba(0,0,0,0.1) !important\",\r\n borderRadius: \"5px\",\r\n backgroundColor: \"#FFFFFF\",\r\n },\r\n actionButton: {\r\n width: \"128px\",\r\n height: \"40px\",\r\n textTransform: \"none\",\r\n },\r\n accountTitle: {\r\n fontWeight: 300,\r\n width: \"60%\",\r\n maxWidth: \"500px\",\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n },\r\n}));\r\n","import { makeStyles } from \"@material-ui/core\";\r\n\r\nexport const useStyles = makeStyles((theme) => ({\r\n button: {\r\n width: 85,\r\n fontWeight: 600,\r\n textTransform: 'none'\r\n },\r\n buttonActive: {\r\n width: 85,\r\n backgroundColor: '#EEEEEE',\r\n fontWeight: 600,\r\n textTransform: 'none'\r\n }\r\n}));\r\n","import React, { useContext, useState } from \"react\";\r\nimport { Box } from \"@material-ui/core\";\r\nimport \"./LayoutTopSectionColumn.scss\";\r\n\r\ninterface Props {\r\n children: React.ReactNode;\r\n title?: string;\r\n icon?: React.ReactNode;\r\n padLeft?: boolean;\r\n}\r\n\r\nconst LayoutTopSectionColumn = (props: Props) => {\r\n const { title, icon, children, padLeft = false } = props;\r\n return (\r\n \r\n \r\n {icon}\r\n \r\n {title}\r\n \r\n \r\n\r\n {children} \r\n \r\n );\r\n};\r\n\r\nexport default LayoutTopSectionColumn;\r\n","import React, { useContext, useState, useEffect } from \"react\";\r\nimport { Box, Button, ButtonGroup, Switch } from \"@material-ui/core\";\r\nimport { useStyles } from \"./CampaignStrategy.styles\";\r\nimport { RootStoreContext } from \"../../../../stores/RootStoreContext\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { EStrategyName } from \"../../../../models/prediction/enums/EStrategyName\";\r\nimport {\r\n biddingStrategyType,\r\n jobExpansionStrategyType,\r\n} from \"../../PredictionPage\";\r\nimport LayoutTopSectionColumn from \"../layout-top-section-column/LayoutTopSectionColumn\";\r\nimport { message } from 'antd';\r\n\r\ninterface Props {\r\n biddingStrategy: biddingStrategyType;\r\n setBiddingStrategy: (v: biddingStrategyType) => void;\r\n jobExpansionStrategy: jobExpansionStrategyType;\r\n setJobExpansionStrategy: (v: jobExpansionStrategyType) => void;\r\n}\r\n\r\ntype strategy = \"bidding\" | \"job expansion\";\r\n// type biddingStrategyType = \"normal\" | \"high\";\r\n// type jobExpansionStrategyType = \"with\" | \"without\";\r\n\r\nconst CampaignStrategy = (props: Props) => {\r\n const [blockStrategyChange, setBlockStrategyChange] = useState(false);\r\n const blockChangeTimeInMilliseconds = 650;\r\n\r\n const {\r\n biddingStrategy,\r\n setBiddingStrategy,\r\n jobExpansionStrategy,\r\n setJobExpansionStrategy,\r\n } = props;\r\n const classes = useStyles();\r\n const { predictionStore } = useContext(RootStoreContext);\r\n\r\n const isStrategyActive = (strategy: strategy, strategyText: string) => {\r\n if (strategy === \"bidding\") {\r\n if (strategyText === \"high\") {\r\n return (\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.HighBid ||\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n } else {\r\n // \"normal\"\r\n return (\r\n predictionStore.activeCampaignStrategy !==\r\n EStrategyName.HighBid &&\r\n predictionStore.activeCampaignStrategy !==\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n }\r\n } else {\r\n // strategy == \"job expansion\"\r\n if (strategyText === \"with\") {\r\n return (\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.Expansions ||\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n } else {\r\n return (\r\n predictionStore.activeCampaignStrategy !==\r\n EStrategyName.Expansions &&\r\n predictionStore.activeCampaignStrategy !==\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n }\r\n }\r\n };\r\n\r\n const onChangeStrategy = (type: strategy, value: boolean) => (\r\n e: React.MouseEvent\r\n ) => {\r\n\r\n if(blockStrategyChange) {\r\n message.warn(\"Please wait half a second. You can change a strategy every 0.5 seconds\")\r\n return;\r\n }\r\n setBlockStrategyChange(true);\r\n\r\n if (type == \"bidding\") {\r\n if (value && biddingStrategy === \"high\") return;\r\n if (!value && biddingStrategy === \"normal\") return;\r\n\r\n if (value) {\r\n setBiddingStrategy(\"high\");\r\n if (jobExpansionStrategy === \"yes\") {\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n return;\r\n }\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.HighBid\r\n );\r\n return;\r\n }\r\n\r\n setBiddingStrategy(\"normal\");\r\n if (jobExpansionStrategy === \"yes\") {\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.Expansions\r\n );\r\n return;\r\n }\r\n predictionStore.SetActiveCampaignStrategy(EStrategyName.Normal);\r\n }\r\n if (type == \"job expansion\") {\r\n if (value && jobExpansionStrategy === \"yes\") return;\r\n if (!value && jobExpansionStrategy === \"no\") return;\r\n\r\n if (value) {\r\n setJobExpansionStrategy(\"yes\");\r\n if (biddingStrategy === \"high\") {\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.HighBidAndExpansions\r\n );\r\n return;\r\n }\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.Expansions\r\n );\r\n return;\r\n }\r\n\r\n setJobExpansionStrategy(\"no\");\r\n if (biddingStrategy === \"high\") {\r\n predictionStore.SetActiveCampaignStrategy(\r\n EStrategyName.HighBid\r\n );\r\n return;\r\n }\r\n predictionStore.SetActiveCampaignStrategy(EStrategyName.Normal);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n if(blockStrategyChange) {\r\n window.setTimeout(() => {\r\n setBlockStrategyChange(false);\r\n }, blockChangeTimeInMilliseconds)\r\n }\r\n }, [blockStrategyChange])\r\n\r\n return (\r\n \r\n \r\n Campaign Strategy\r\n \r\n\r\n \r\n \r\n \r\n \r\n Normal\r\n \r\n \r\n High\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n Yes\r\n \r\n \r\n No\r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default observer(CampaignStrategy);\r\n","import { makeStyles } from \"@material-ui/core\";\r\n\r\nexport const useStyles = makeStyles((theme) => ({\r\n blackIcon: {\r\n color: \"#485465\",\r\n },\r\n dateSelect: {\r\n width: 55,\r\n fontSize: \"25px\",\r\n //paddingLeft: \"5px\",\r\n fontWeight: 300,\r\n color: \"#485465\",\r\n fontFamily: \"Comfortaa\",\r\n },\r\n dateDiv: {\r\n [theme.breakpoints.down(1400)]: {\r\n fontSize: '20px'\r\n },\r\n },\r\n}));\r\n","import React, { useContext, useState } from \"react\";\r\nimport { Box, Button, MenuItem, Select } from \"@material-ui/core\";\r\nimport { useStyles } from \"./ReportDetails.styles\";\r\nimport EventIcon from \"@material-ui/icons/Event\";\r\nimport ScheduleIcon from \"@material-ui/icons/Schedule\";\r\nimport AdjustOutlinedIcon from \"@material-ui/icons/AdjustOutlined\";\r\nimport { RootStoreContext } from \"../../../../stores/RootStoreContext\";\r\nimport moment from \"moment\";\r\nimport { NumbersHelper } from \"../../../../services/helpers/NumbersHelper\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { EPredictionType } from \"../../../../models/prediction/UserPredictionRequest\";\r\nimport LayoutTopSectionColumn from \"../layout-top-section-column/LayoutTopSectionColumn\";\r\n\r\ninterface Props { }\r\n\r\nconst ReportDetails = (props: Props) => {\r\n const { predictionStore } = useContext(RootStoreContext);\r\n\r\n const classes = useStyles();\r\n\r\n return (\r\n \r\n \r\n Report Details\r\n \r\n\r\n \r\n }\r\n title=\"Goal\">\r\n {predictionStore.goalText \r\n ?\r\n \r\n {predictionStore.goalText}\r\n \r\n :\r\n \r\n {predictionStore.userPredictionRequest!.amount &&\r\n predictionStore.userPredictionRequest!\r\n .predictionType === EPredictionType.Budget\r\n ? \"$\"\r\n : \"\"}\r\n {predictionStore.userPredictionRequest!.amount\r\n ? NumbersHelper.numberWithCommas(\r\n predictionStore.userPredictionRequest!.amount\r\n )\r\n : \"-\"}{\" \"}\r\n \r\n {predictionStore.userPredictionRequest!.amount\r\n ? predictionStore.userPredictionRequest!\r\n .predictionType\r\n : \"\"}\r\n \r\n \r\n }\r\n \r\n\r\n }\r\n padLeft>\r\n \r\n {moment(new Date()).format(\"DD-MM-YYYY\")}\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default observer(ReportDetails);\r\n","import React from \"react\";\r\nimport { Box, Button } from \"@material-ui/core\";\r\nimport { MapPoint } from \"../../../../../stores/prediction/datamodels/MapPoint\";\r\nimport { colorsDictionary } from \"../JobsMap\";\r\n\r\ninterface Props {}\r\nexport const PopupContentRow = (title: string, content: string | number) => {\r\n return (\r\n \r\n \r\n {title} \r\n {content}
\r\n \r\n \r\n );\r\n};\r\n\r\nexport const PopupContentTitle = (title: string) => {\r\n return (\r\n \r\n \r\n Jobs by Competition:\r\n \r\n \r\n );\r\n};\r\n\r\nexport const CreateContentRows = (point: MapPoint) => {\r\n return (\r\n <>\r\n {PopupContentRow(\"Jobs\", point.totaljobs)}\r\n\r\n {PopupContentTitle(\"Jobs by Competition:\")}\r\n {PopupContentRow(\"Low\", point.totalLowCompetition)}\r\n {PopupContentRow(\"Medium\", point.totalMediumCompetition)}\r\n {PopupContentRow(\"High\", point.totalHighCompetition)}\r\n >\r\n );\r\n};\r\n\r\nexport const PopupTitle = (title: string, category: string) => {\r\n return (\r\n \r\n \r\n {title}\r\n \r\n \r\n );\r\n};\r\n","import React from \"react\";\r\nimport { MapPoint } from \"../../../../../stores/prediction/datamodels/MapPoint\";\r\nimport ReactMapGL, { Popup } from \"react-map-gl\";\r\nimport {\r\n PopupTitle,\r\n PopupContentRow,\r\n CreateContentRows,\r\n} from \"./LocationPopup.factory\";\r\nimport { observer } from \"mobx-react-lite\";\r\n\r\ninterface Props {\r\n selectedJob: MapPoint | null;\r\n setSelectedJob: (job: MapPoint | null) => void;\r\n}\r\n\r\nconst LocationPopup = (props: Props) => {\r\n const { selectedJob, setSelectedJob } = props;\r\n if (!selectedJob) return <>>;\r\n\r\n return (\r\n {\r\n setSelectedJob(null);\r\n }}>\r\n \r\n {PopupTitle(\r\n selectedJob.location,\r\n selectedJob.competitionCategory\r\n )}\r\n {CreateContentRows(selectedJob)}\r\n
\r\n \r\n );\r\n};\r\n\r\nexport default observer(LocationPopup);\r\n","import mapboxgl from \"mapbox-gl\";\r\nimport React, { useState, useEffect, useContext, useMemo } from \"react\";\r\nimport ReactMapGL from \"react-map-gl\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { RootStoreContext } from \"../../../../stores/RootStoreContext\";\r\nimport { MapPoint } from \"../../../../stores/prediction/datamodels/MapPoint\";\r\nimport LocationPopup from \"./components/LocationPopup\";\r\nimport { CreateIconsOnMap } from \"./JobsMap.factory\";\r\nimport \"./JobsMap.scss\";\r\n\r\n/*\r\nresources: https://github.com/leighhalliday/mapbox-react-demo/blob/master/src/App.js\r\nhttps://www.youtube.com/watch?v=JJatzkPcmoI\r\n*/\r\n\r\nexport const colorsDictionary: any = {\r\n Low: \"#008450\",\r\n Medium: \"#efb700\",\r\n High: \"#b81d13\",\r\n};\r\n\r\nconst ACCESS_TOKEN =\r\n \"pk.eyJ1IjoibmFkYXJhIiwiYSI6ImNrbnJlZWwzcDI1ZnMydXBlbG95eDZ2MGYifQ.bHgDDmozMa_8MNeFMUf5iw\";\r\nconst MAP_STYLE = \"mapbox://styles/nadara/cknrf3x8w06ao17pqvbmmbn93\";\r\n\r\ninterface Props {}\r\nconst JobsMap = (props: Props) => {\r\n const { predictionStore } = useContext(RootStoreContext);\r\n\r\n const [viewport, setViewport] = useState({\r\n latitude: predictionStore.predictionResult!.predictions[0].lat,\r\n longitude: predictionStore.predictionResult!.predictions[0].long,\r\n width: \"100%\",\r\n height: \"100%\",\r\n zoom: 3,\r\n });\r\n const [selectedJob, setSelectedJob] = useState(null);\r\n\r\n const mapData: MapPoint[] = useMemo(() => {\r\n return predictionStore.GetJobsDisplayedOnMap;\r\n }, [predictionStore.filteredPredictions]);\r\n\r\n if (!predictionStore.GetJobsDisplayedOnMap) return <>>;\r\n\r\n return (\r\n {\r\n setViewport(viewport);\r\n }}\r\n onClick={(e) => {\r\n setSelectedJob(null);\r\n }}>\r\n {CreateIconsOnMap(mapData, (job) => {\r\n setSelectedJob(job);\r\n })}\r\n\r\n \r\n \r\n );\r\n};\r\nexport default React.memo(observer(JobsMap));\r\n\r\n// @ts-ignore\r\n// eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved\r\nmapboxgl.workerClass = require(\"worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker\").default;\r\n","import React from \"react\";\r\nimport { MapPoint } from \"../../../../stores/prediction/datamodels/MapPoint\";\r\nimport FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';\r\nimport WorkIcon from \"@material-ui/icons/Work\";\r\nimport { colorsDictionary } from \"./JobsMap\";\r\nimport ReactMapGL, { Marker } from \"react-map-gl\";\r\n\r\nexport const CreateIconsOnMap = (\r\n getJobsDisplayedOnMap: MapPoint[],\r\n onIconClick: (job: MapPoint) => void\r\n): any => {\r\n return getJobsDisplayedOnMap.map((job, index: number) => (\r\n \r\n {\r\n e.preventDefault();\r\n onIconClick(job);\r\n }}\r\n style={{\r\n cursor: \"pointer\",\r\n color: colorsDictionary[job.competitionCategory],\r\n // color: \"#225A78\",\r\n width: Math.min(50, 20 + job.totaljobs * 0.125),\r\n height: Math.min(50, 20 + job.totaljobs * 0.125),\r\n }}\r\n />\r\n \r\n ));\r\n};\r\n","export enum EGrantType {\r\n SalesMan = 1,\r\n Admin = 2\r\n}\r\n\r\n\r\nexport class User {\r\n /**\r\n *\r\n */\r\n constructor(\r\n public userId: number,\r\n public name: string,\r\n public email: string,\r\n public password: string,\r\n public GrantType: EGrantType,\r\n ) {\r\n\r\n }\r\n}","import { makeStyles } from \"@material-ui/core\";\r\n\r\nexport const useStyles = makeStyles((theme) => ({\r\n blackIcon: {\r\n color: \"#485465\",\r\n },\r\n dateSelect: {\r\n width: 55,\r\n fontSize: \"14px\",\r\n fontWeight: 300,\r\n color: \"#485465\",\r\n fontFamily: \"Comfortaa\",\r\n },\r\n resetButton: {\r\n \"&.disabled\": {\r\n borderColor: \"#C2C3C4 !important\",\r\n color: \"#C2C3C4 !important\",\r\n pointerEvents: \"none !important\",\r\n },\r\n },\r\n}));\r\n","import { PredictionSettings } from \"../../../../stores/prediction/datamodels/PredictionSettings\";\r\nimport { NumbersHelper } from '../../../../services/helpers/NumbersHelper';\r\n\r\n\r\nexport class SettingsRules {\r\n /**\r\n *\r\n */\r\n constructor(\r\n ) {\r\n\r\n }\r\n\r\n isValidInput(\r\n cvr: string,\r\n cvh: string,\r\n duration: number,\r\n predictionSettings: PredictionSettings,\r\n defaultPredictionSettings: PredictionSettings\r\n ): [boolean, \"error\" | \"warning\" | \"info\", string] {\r\n if (NumbersHelper.isNumber(cvh) == false && cvh !== \"\") {\r\n return [false, \"error\", \"Please make sure app. to hires ratio is a valid number\"];\r\n\r\n }\r\n if (NumbersHelper.isNumber(cvr) == false && cvr !== \"\") {\r\n return [false, \"error\", \"Please make sure clicks to app. ratio is a valid number\"]\r\n }\r\n\r\n if (\r\n (Number(cvr) || undefined) === predictionSettings.cvr &&\r\n (Number(cvh) || undefined) === predictionSettings.cvh &&\r\n duration === predictionSettings.predictionDuration\r\n ) {\r\n return [false, \"warning\", \"You didn't change any setting\"]\r\n }\r\n\r\n if ((Number(cvh) > 100 || Number(cvh) < 0) || (Number(cvr) > 100 || Number(cvr) < 0)) {\r\n return [false, \"error\", \"Make sure cvh and cpa are valid numbers between 0 to 100\"]\r\n }\r\n return [true, \"info\", \"\"];\r\n\r\n\r\n }\r\n}","import React, { useContext, useState } from \"react\";\r\nimport { Box, Button, MenuItem, Select } from \"@material-ui/core\";\r\nimport { useStyles } from \"./Settings.styles\";\r\nimport { RootStoreContext } from \"../../../../stores/RootStoreContext\";\r\nimport moment from \"moment\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport ScheduleIcon from \"@material-ui/icons/Schedule\";\r\nimport { FaPercent } from \"react-icons/fa\";\r\nimport Input from \"../../../common/inputs/input/Input\";\r\nimport LayoutTopSectionColumn from \"../layout-top-section-column/LayoutTopSectionColumn\";\r\nimport {\r\n predictionDuration,\r\n PredictionSettings,\r\n} from \"../../../../stores/prediction/datamodels/PredictionSettings\";\r\nimport { message } from \"antd\";\r\nimport { SettingsRules } from \"./SettingsRules\";\r\n\r\ninterface Props {}\r\n\r\nconst Settings = (props: Props) => {\r\n /* App State\r\n * ################################################\r\n */\r\n const { predictionStore } = useContext(RootStoreContext);\r\n\r\n /* Local State\r\n * ################################################\r\n */\r\n const _initialCvr =\r\n (predictionStore.defaultPredictionSettings.cvr || 0).toFixed(2) ?? \"\";\r\n\r\n const _initialCvh =\r\n predictionStore.defaultPredictionSettings.cvh?.toString() ?? \"\";\r\n\r\n const [cvr, setCvr] = useState(_initialCvr);\r\n const [cvh, setCvh] = useState(_initialCvh);\r\n const [duration, setDuration] = useState(\r\n predictionStore.defaultPredictionSettings.predictionDuration\r\n );\r\n\r\n /* Variables\r\n * ################################################\r\n */\r\n const classes = useStyles();\r\n const settingsRules = new SettingsRules();\r\n\r\n /* Logic\r\n * ################################################\r\n */\r\n const handleReset = () => {\r\n setCvr(_initialCvr);\r\n setCvh(_initialCvh);\r\n setDuration(\r\n predictionStore.defaultPredictionSettings.predictionDuration\r\n );\r\n const predictionSettings = new PredictionSettings(30);\r\n predictionStore.SetPredictionSettings(predictionSettings);\r\n message.info(\"Restored default settings\");\r\n };\r\n\r\n const handleApply = () => {\r\n const [isValid, severity, errorMessage] = settingsRules.isValidInput(\r\n cvr,\r\n cvh,\r\n duration,\r\n predictionStore.predictionSettings,\r\n predictionStore.defaultPredictionSettings\r\n );\r\n if (isValid === false) {\r\n message[severity](errorMessage);\r\n return;\r\n }\r\n const predictionSettings = new PredictionSettings(\r\n duration,\r\n cvr ? Number(cvr) : undefined,\r\n cvh ? Number(cvh) : undefined\r\n );\r\n predictionStore.SetPredictionSettings(predictionSettings);\r\n message.info(\"Applied settings filter succesfully\");\r\n };\r\n\r\n const getClassName = (forElement: \"reset-btn\") => {\r\n let className = \"\";\r\n const appendClassName = (_className: string) => {\r\n className += `${_className} `;\r\n };\r\n\r\n if (forElement === \"reset-btn\") {\r\n appendClassName(classes.resetButton);\r\n if (!cvr && !cvh && duration === 30) {\r\n appendClassName(\"disabled\");\r\n }\r\n }\r\n\r\n return className;\r\n };\r\n\r\n /* Render\r\n * ################################################\r\n */\r\n return (\r\n \r\n \r\n Settings\r\n \r\n\r\n \r\n }>\r\n \r\n {\r\n const _duration: predictionDuration = Number(\r\n e.target.value\r\n ) as predictionDuration;\r\n // predictionStore.setPredictionDuration(duration);\r\n setDuration(_duration);\r\n }}>\r\n 30 \r\n 14 \r\n 7 \r\n \r\n \r\n Days\r\n \r\n \r\n \r\n\r\n }\r\n padLeft>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n Apply\r\n \r\n \r\n \r\n \r\n RESET\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default observer(Settings);\r\n","import { ValueFormatterParams } from 'ag-grid-community';\r\nimport { PredictionGridModel } from '../../../../stores/prediction/datamodels/PredictionGridModel';\r\n\r\nexport const gridColumns = [\r\n {\r\n groupName: \"Job Details\",\r\n groupData: [\r\n {\r\n field: \"jobId\",\r\n headerName: \"Job Id\",\r\n properties: {\r\n \r\n },\r\n },\r\n {\r\n field: \"jobTitle\",\r\n headerName: \"Job Title\",\r\n properties: {\r\n cellClass: \"ag-link-column\",\r\n onCellClicked: (event: any) => {\r\n const data: PredictionGridModel = event.data;\r\n window.open(data.externalApplyUrl, \"_blank\");\r\n },\r\n },\r\n },\r\n {\r\n field: \"location\",\r\n headerName: \"Location\",\r\n properties: {\r\n \r\n },\r\n\r\n },\r\n {\r\n field: \"category\",\r\n headerName: \"Category\",\r\n properties: {\r\n headerClass: \"ag-right-border-column\",\r\n cellClass: \"ag-right-border-column\",\r\n \r\n },\r\n },\r\n ]\r\n },\r\n {\r\n groupName: \"Predicted Performance\",\r\n groupData: [\r\n {\r\n field: \"clicks\",\r\n headerName: \"Clicks\",\r\n properties: {\r\n type: \"rightAligned\",\r\n },\r\n },\r\n {\r\n field: \"applicants\",\r\n headerName: \"Applicants\",\r\n properties: {\r\n type: \"rightAligned\",\r\n // filter: \"agMultiColumnFilter\",\r\n },\r\n },\r\n {\r\n field: \"conversionRate\",\r\n headerName: \"Conv Rate\",\r\n properties: {\r\n valueFormatter: (params: ValueFormatterParams) => `${params.value}%`,\r\n type: \"rightAligned\",\r\n // filter: \"agMultiColumnFilter\",\r\n // filterParams: {\r\n // filters: [\r\n // {\r\n // filter: 'agNumberColumnFilter',\r\n\r\n // },\r\n // {\r\n // filter: 'agSetColumnFilter',\r\n // display: 'subMenu',\r\n // }\r\n // ]\r\n // }\r\n },\r\n\r\n },\r\n {\r\n field: \"budget\",\r\n headerName: \"Est. Cost\",\r\n properties: {\r\n valueFormatter: (params: ValueFormatterParams) => `$${params.value}`,\r\n type: \"rightAligned\",\r\n // filter: \"agMultiColumnFilter\",\r\n // filterParams: {\r\n // filters: [\r\n // {\r\n // filter: 'agNumberColumnFilter',\r\n\r\n // },\r\n // {\r\n // filter: 'agSetColumnFilter',\r\n // display: 'subMenu',\r\n // }\r\n // ]\r\n // }\r\n },\r\n },\r\n {\r\n field: \"cpc\",\r\n headerName: \"Avg CPC\",\r\n properties: {\r\n valueFormatter: (params: ValueFormatterParams) => `$${params.value}`,\r\n type: \"rightAligned\",\r\n // filter: \"agMultiColumnFilter\",\r\n // filterParams: {\r\n // filters: [\r\n // {\r\n // filter: 'agNumberColumnFilter',\r\n\r\n // },\r\n // {\r\n // filter: 'agSetColumnFilter',\r\n // display: 'subMenu',\r\n // }\r\n // ]\r\n // }\r\n },\r\n },\r\n {\r\n field: \"cpa\",\r\n headerName: \"Avg CPA\",\r\n properties: {\r\n valueFormatter: (params: ValueFormatterParams) => `$${params.value}`,\r\n type: \"rightAligned\",\r\n // filter: \"agMultiColumnFilter\",\r\n // filterParams: {\r\n // filters: [\r\n // {\r\n // filter: 'agNumberColumnFilter',\r\n\r\n // },\r\n // {\r\n // filter: 'agSetColumnFilter',\r\n // display: 'subMenu',\r\n // }\r\n // ]\r\n // },\r\n },\r\n },\r\n ]\r\n },\r\n {\r\n groupName: \"Market Indicators\",\r\n groupData: [\r\n {\r\n field: \"clickRange\",\r\n headerName: \"Click Range\",\r\n properties: {\r\n headerClass: \"ag-left-border-column\",\r\n cellClass: \"ag-left-border-column\",\r\n filter: \"agSetColumnFilter\",\r\n // filterParams: {\r\n // excelMode: 'mac',\r\n // },\r\n },\r\n },\r\n {\r\n field: \"competition\",\r\n headerName: \"Competition\",\r\n properties: {\r\n filter: \"agSetColumnFilter\",\r\n // filterParams: {\r\n // excelMode: 'mac',\r\n // },\r\n },\r\n },\r\n {\r\n field: \"demandFactor\",\r\n headerName: \"Demand\",\r\n properties: {\r\n filter: \"agSetColumnFilter\",\r\n // filterParams: {\r\n // excelMode: 'mac',\r\n // },\r\n },\r\n },\r\n {\r\n field: \"supplyFactor\",\r\n headerName: \"Supply\",\r\n properties: {\r\n headerClass: \"ag-right-border-column\",\r\n cellClass: \"ag-right-border-column\",\r\n filter: \"agSetColumnFilter\",\r\n // filterParams: {\r\n // excelMode: 'mac',\r\n // },\r\n },\r\n },\r\n ]\r\n },\r\n]","import React, { useState, useContext, useEffect, useRef } from \"react\";\r\nimport { AgGridColumn, AgGridReact } from \"ag-grid-react\";\r\nimport { RootStoreContext } from \"../../../../stores/RootStoreContext\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { gridColumns } from \"./gridColumns\";\r\nimport \"ag-grid-community\";\r\nimport \"ag-grid-enterprise\";\r\nimport \"./ag-grid-custom.scss\";\r\n\r\nconst JobsGrid = () => {\r\n const { predictionStore } = useContext(RootStoreContext);\r\n // const gridData = useRef(predictionStore.GetGridData());\r\n const gridRef = useRef(null);\r\n const competitionScoreChartRef = useRef(null);\r\n const clickRangeChartRef = useRef(null);\r\n const [gridData, setGridData] = useState([]);\r\n\r\n //#region Charts Logic\r\n const createCompetitionScoreChart = (params, withDestroy) => {\r\n if (withDestroy) {\r\n competitionScoreChartRef.current.destroyChart();\r\n }\r\n\r\n const competitionScoreChart = {\r\n cellRange: {\r\n columns: [\"jobId\", \"competition\"],\r\n },\r\n chartType: \"groupedBar\",\r\n chartContainer: document.querySelector(\"#competitionScoreChart\"),\r\n aggFunc: \"count\",\r\n chartThemeOverrides: {\r\n common: {\r\n title: {\r\n text: \"Jobs by Competition Score\",\r\n },\r\n tooltipClass: \"my-tooltip-class\",\r\n },\r\n },\r\n };\r\n competitionScoreChartRef.current = params.api.createRangeChart(\r\n competitionScoreChart\r\n );\r\n };\r\n\r\n const createClickRangeChart = (params, withDestroy) => {\r\n if (withDestroy) {\r\n clickRangeChartRef.current.destroyChart();\r\n }\r\n const clickRangeChart = {\r\n cellRange: {\r\n columns: [\"jobId\", \"clickRange\"],\r\n },\r\n chartType: \"groupedBar\",\r\n chartContainer: document.querySelector(\"#clickRangeChart\"),\r\n aggFunc: \"count\",\r\n chartThemeOverrides: {\r\n common: {\r\n title: {\r\n text: \"Jobs by Click Range Score\",\r\n },\r\n tooltipClass: \"my-tooltip-class\",\r\n },\r\n },\r\n };\r\n clickRangeChartRef.current =\r\n params.api.createRangeChart(clickRangeChart);\r\n };\r\n\r\n const onFirstDataRendered = (params) => {\r\n createCompetitionScoreChart(params, false);\r\n createClickRangeChart(params, false);\r\n\r\n params.api.deselectAll();\r\n params.api.clearRangeSelection();\r\n };\r\n\r\n const customChartThemes = {\r\n myCustomTheme: {\r\n baseTheme: \"ag-pastel\",\r\n palette: {\r\n fills: [\"#30789F\", \"#a2bf8a\", \"#ebcc87\"],\r\n strokes: [\"#225A78\", \"#718661\", \"#a48f5f\"],\r\n },\r\n overrides: {\r\n common: {\r\n padding: {\r\n top: 20,\r\n },\r\n title: {\r\n enabled: true,\r\n fontSize: 28,\r\n fontFamily: \"Open Sans, sans-serif\",\r\n paddingY: 40,\r\n color: \"#225A78\",\r\n texAlign: \"left\",\r\n },\r\n legend: {\r\n enabled: false,\r\n item: {\r\n label: {\r\n fontStyle: \"italic\",\r\n fontWeight: \"bold\",\r\n fontSize: 25,\r\n fontFamily: \"Palatino, serif\",\r\n color: \"#485465\",\r\n },\r\n },\r\n },\r\n },\r\n },\r\n },\r\n };\r\n //#endregion\r\n\r\n const sideBarConfiguration = {\r\n toolPanels: [\r\n {\r\n id: \"filters\",\r\n labelDefault: \"Filters\",\r\n labelKey: \"filters\",\r\n iconKey: \"filter\",\r\n toolPanel: \"agFiltersToolPanel\",\r\n },\r\n {\r\n id: \"columns\",\r\n labelDefault: \"Columns\",\r\n labelKey: \"columns\",\r\n iconKey: \"columns\",\r\n toolPanel: \"agColumnsToolPanel\",\r\n },\r\n ],\r\n // defaultToolPanel: \"filters\",\r\n position: \"right\",\r\n };\r\n\r\n useEffect(() => {\r\n setGridData(predictionStore.GetGridData());\r\n }, [\r\n predictionStore.activeCampaignStrategy,\r\n predictionStore.predictionSettings,\r\n ]);\r\n\r\n return (\r\n \r\n \r\n
{\r\n e.api.sizeColumnsToFit();\r\n }}\r\n onFilterChanged={(e) => {\r\n if (predictionStore.gridFilterOptions.useOldFilter) {\r\n // console.log(\r\n // predictionStore.gridFilterOptions\r\n // .agGridFilterModel\r\n // );\r\n\r\n const filteredColumns = Object.keys(\r\n predictionStore.gridFilterOptions\r\n .agGridFilterModel\r\n );\r\n // console.log(filteredColumns);\r\n filteredColumns.forEach((column) => {\r\n const columnInstance =\r\n e.api.getFilterInstance(column);\r\n columnInstance.setModel(\r\n predictionStore.gridFilterOptions\r\n .agGridFilterModel[column]\r\n );\r\n columnInstance.applyModel();\r\n });\r\n e.api.onFilterChanged();\r\n\r\n predictionStore.gridFilterOptions.setUseOldFilterAs(\r\n false\r\n );\r\n return;\r\n }\r\n\r\n const filterModel = e.api.getFilterModel();\r\n predictionStore.gridFilterOptions.setUseOldFilterAs(\r\n false\r\n );\r\n\r\n const includedPredictionsAfterFilter = {};\r\n e.api.forEachNodeAfterFilter((rowNode) => {\r\n includedPredictionsAfterFilter[\r\n rowNode.data.jobId.toString()\r\n ] = true;\r\n });\r\n\r\n if (\r\n Object.keys(includedPredictionsAfterFilter)\r\n .length == 0\r\n ) {\r\n includedPredictionsAfterFilter[\"empty\"] = true;\r\n }\r\n\r\n // console.log(\r\n // includedPredictionsAfterFilter,\r\n // Object.keys(includedPredictionsAfterFilter).length\r\n // );\r\n\r\n predictionStore.SetFilteredPredictions(\r\n includedPredictionsAfterFilter\r\n );\r\n predictionStore.setGridFilterOptions(\r\n includedPredictionsAfterFilter,\r\n filterModel\r\n );\r\n\r\n createClickRangeChart(e, true);\r\n createCompetitionScoreChart(e, true);\r\n // e.api.deselectAll();\r\n // e.api.clearRangeSelection();\r\n }}\r\n chartThemeOverrides={{\r\n bar: {\r\n series: {\r\n fillOpacity: 1,\r\n strokeOpacity: 1,\r\n strokeWidth: 2,\r\n shadow: {\r\n enabled: true,\r\n color: \"rgba(0, 0, 0, 0.3)\",\r\n xOffset: 10,\r\n yOffset: 5,\r\n blur: 8,\r\n },\r\n highlightStyle: {\r\n fill: \"#225A78\",\r\n stroke: \"#225A78\",\r\n },\r\n },\r\n },\r\n histogram: {\r\n series: {\r\n fillOpacity: 1,\r\n strokeOpacity: 1,\r\n strokeWidth: 2,\r\n shadow: {\r\n enabled: true,\r\n color: \"rgba(0, 0, 0, 0.3)\",\r\n xOffset: 10,\r\n yOffset: 5,\r\n blur: 8,\r\n },\r\n highlightStyle: {\r\n fill: \"#30789F\",\r\n stroke: \"#30789F\",\r\n },\r\n },\r\n },\r\n }}\r\n autoSizePadding\r\n onFirstDataRendered={onFirstDataRendered}>\r\n {createGrid(gridColumns)}\r\n \r\n
\r\n \r\n );\r\n};\r\n\r\n//#region Helper Components\r\n\r\nconst createGrid = (gridColumns) => {\r\n return gridColumns.map((group) =>\r\n createGridGroup(group.groupName, group.groupData)\r\n );\r\n};\r\n\r\nconst createGridGroup = (title, groupColumns) => {\r\n return (\r\n \r\n {groupColumns.map((columnData) => (\r\n \r\n ))}\r\n \r\n );\r\n};\r\n\r\n//#endregion\r\n\r\nexport default observer(JobsGrid);\r\n","\r\n\r\n/**\r\n * A class that triggers downloads in JavaScript browser\r\n * API example new FileDownloader().Download(datasourceFunc, fileName);\r\n */\r\nexport class FileDownloader {\r\n /**\r\n *\r\n */\r\n constructor() { }\r\n\r\n\r\n public DownloadFromBase64(base64string: string, fileName: string) {\r\n const byteCharacters = atob(base64string);\r\n const byteNumbers = new Array(byteCharacters.length);\r\n for (let i = 0; i < byteCharacters.length; i++) {\r\n byteNumbers[i] = byteCharacters.charCodeAt(i);\r\n }\r\n const byteArray = new Uint8Array(byteNumbers);\r\n this.DownloadFromBytes(byteArray, fileName);\r\n }\r\n\r\n public DownloadFromBytes(byteArray: Uint8Array | Uint16Array | ArrayBuffer, fileName: string) {\r\n const blob = new Blob([new Uint8Array(byteArray)]);\r\n this.DownloadFromBlob(blob, fileName);\r\n }\r\n\r\n public DownloadFromBlob(blob: Blob, fileName: string) {\r\n const url = window.URL.createObjectURL(blob);\r\n const link = document.createElement(\"a\") as any;\r\n link.href = url;\r\n link.setAttribute(\"download\", fileName); \r\n document.body.appendChild(link);\r\n link.click();\r\n }\r\n}","import { EStrategyName } from \"./enums/EStrategyName\";\r\nimport { Prediction } from \"./Prediction\";\r\nimport { EPredictionType } from \"./UserPredictionRequest\";\r\nimport { PredictionGridModel } from './../../stores/prediction/datamodels/PredictionGridModel';\r\nimport { PredictionSummary } from './../../stores/prediction/datamodels/PredictionSummary';\r\n\r\nexport class UserFilterResult {\r\n /**\r\n *\r\n */\r\n constructor(\r\n public batchId: string,\r\n public accountId: number,\r\n public accountName: string,\r\n public gridData: PredictionGridModel[],\r\n public predictionSummary: PredictionSummary,\r\n public strategyName: EStrategyName,\r\n public predictionType: EPredictionType,\r\n public durationInDays: number,\r\n public goalText: string | null,\r\n ) {\r\n }\r\n}","\r\nexport class CreateExcelFromBatchRequest {\r\n /**\r\n * ctor\r\n */\r\n constructor(\r\n public batchId: string,\r\n ) {} \r\n}","import React, { useState, useContext, useEffect } from \"react\";\r\nimport axios from \"axios\";\r\nimport { Box, Button } from \"@material-ui/core\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\nimport HorizontalDivider from \"../common/horizontal-divider/HorizontalDivider\";\r\nimport InformationBox from \"./components/information-box/InformationBox\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { RootStoreContext } from \"./../../stores/RootStoreContext\";\r\nimport VerticalDivider from \"../common/vertical-divider/VerticalDivider\";\r\nimport { useStyles } from \"./PredictionPage.styles\";\r\nimport CampaignStrategy from \"./components/campaign-strategy/CampaignStrategy\";\r\nimport ReportDetails from \"./components/report-details/ReportDetails\";\r\nimport EditIcon from \"@material-ui/icons/Edit\";\r\nimport downlodFilePng from \"../../assets/images/file-download-x.png\";\r\nimport useSetCurrentPage from \"../../hooks/useSetCurrentPage\";\r\nimport { Redirect, RouteComponentProps } from \"react-router-dom\";\r\nimport { Routes } from \"../../constants/Routes\";\r\nimport { NumbersHelper } from \"../../services/helpers/NumbersHelper\";\r\nimport { withRouter } from \"react-router\";\r\nimport { EStrategyName } from \"../../models/prediction/enums/EStrategyName\";\r\nimport { StringHelper } from \"../../services/StringHelper\";\r\nimport JobsMap from \"./components/jobs-map/JobsMap\";\r\nimport Settings from \"./components/settings-tab/Settings\";\r\nimport JobsGrid from \"./components/jobs-grid/JobsGrid\";\r\nimport PredictionService from \"./../../services/prediction/PredictionService\";\r\nimport { CreatePredictionsExcelRequest } from \"./../../models/prediction/CreatePredictionsExcelRequest\";\r\nimport { Prediction } from \"./../../models/prediction/Prediction\";\r\nimport { useSnackbar } from \"notistack\";\r\nimport { FileDownloader } from \"../../services/FileDownloader\";\r\nimport { PredictionSummary } from \"./../../stores/prediction/datamodels/PredictionSummary\";\r\nimport { message } from \"antd\";\r\nimport Loader from \"./../common/feedback/loader/Loader\";\r\nimport { AuditPredictionRequest } from \"../../models/predictionsAudit/AuditPredictionRequest\";\r\nimport { UserFilterResult } from \"./../../models/prediction/UserFilterResult\";\r\nimport { CreateExcelFromBatchRequest } from \"../../models/prediction/CreateExcelFromBatchRequest\";\r\n\r\ninterface Props extends RouteComponentProps {}\r\n\r\ntype buttonType = \"edit\";\r\n\r\nexport type biddingStrategyType = \"normal\" | \"high\";\r\nexport type jobExpansionStrategyType = \"yes\" | \"no\";\r\n\r\nconst PredictionPage = (props: Props) => {\r\n /* Props\r\n * ################################################\r\n */\r\n const { history } = props;\r\n\r\n /* App State\r\n * ################################################\r\n */\r\n const { predictionStore } = useContext(RootStoreContext);\r\n\r\n /* Local State\r\n * ################################################\r\n */\r\n\r\n const [isExcelDownloadInProcess, setIsExcelDownloadInProcess] =\r\n useState(false);\r\n\r\n // #region Campaign Strategy state\r\n const [biddingStrategy, setBiddingStrategy] = useState(\r\n predictionStore.activeCampaignStrategy === EStrategyName.HighBid ||\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.HighBidAndExpansions\r\n ? \"high\"\r\n : \"normal\"\r\n );\r\n const [jobExpansionStrategy, setJobExpansionStrategy] =\r\n useState(\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.Expansions ||\r\n predictionStore.activeCampaignStrategy ===\r\n EStrategyName.HighBidAndExpansions\r\n ? \"yes\"\r\n : \"no\"\r\n );\r\n // #endregion\r\n\r\n /* Variables declaration\r\n * ################################################\r\n */\r\n const classes = useStyles();\r\n const { enqueueSnackbar, closeSnackbar } = useSnackbar();\r\n\r\n /* Effect(s)\r\n * ################################################\r\n */\r\n\r\n useSetCurrentPage();\r\n\r\n useEffect(() => {\r\n return function cleanUp() {\r\n predictionStore.SetFilteredPredictions({ fullList: true });\r\n // predictionStore.setIcludedJobDictionary({\r\n // noneEmpty: true,\r\n // });\r\n };\r\n }, []);\r\n\r\n /* Logic\r\n * ################################################\r\n */\r\n const displayRange = (leftNumber: string, rightNumber: string): string => {\r\n if (\r\n !NumbersHelper.isNumber(leftNumber) &&\r\n !NumbersHelper.isNumber(rightNumber)\r\n ) {\r\n return \"-\";\r\n }\r\n if (leftNumber === \"0\" && rightNumber === \"0\") {\r\n return \"-\";\r\n }\r\n\r\n return `$${leftNumber} - $${rightNumber}`;\r\n };\r\n\r\n const editHandler = (e: React.MouseEvent) => {\r\n history.push(Routes.InputPage);\r\n };\r\n\r\n const returnNumberOrDashIfNaN = (value: string) => {\r\n let valueNormalized = Number(value.replace(/[,%&]/gi, \"\"));\r\n if (isNaN(valueNormalized)) {\r\n return \"-\";\r\n }\r\n return value;\r\n };\r\n\r\n const downloadExcelHandler = async () => {\r\n if (isExcelDownloadInProcess) {\r\n message.error(\"Download already in progress\");\r\n return;\r\n }\r\n\r\n if (predictionStore.GetPredictionSummary.budget === \"0\") {\r\n message.error(\r\n \"You can't download an excel since all the data is filtered out\"\r\n );\r\n return;\r\n }\r\n\r\n setIsExcelDownloadInProcess(true);\r\n enqueueSnackbar(\"Preparing PPR excel for download\", {\r\n variant: \"info\",\r\n });\r\n\r\n const accountId = predictionStore.account!.accountId;\r\n const accountName = predictionStore.account!.accountName;\r\n const predictionsGridData = predictionStore.GetGridData(\r\n predictionStore.filteredPredictions\r\n );\r\n\r\n const summaryModel = predictionStore.GetPredictionSummary;\r\n const activeStrategy =\r\n predictionStore.activeCampaignStrategy as EStrategyName;\r\n const predictionType =\r\n predictionStore.userPredictionRequest!.predictionType;\r\n const durationInDays =\r\n predictionStore.predictionSettings.predictionDuration;\r\n const goalText =\r\n predictionStore.goalText;\r\n\r\n const batchSize = 4000;\r\n const predictionsArrayLength = predictionsGridData.length;\r\n const batchGuid = uuidv4();\r\n\r\n for (let i = 0; i < predictionsArrayLength; i += batchSize) {\r\n let batchEndIndex = i + (batchSize - 1);\r\n batchEndIndex =\r\n predictionsArrayLength > batchEndIndex\r\n ? batchEndIndex\r\n : i + (predictionsArrayLength - i);\r\n // console.log(batchEndIndex);\r\n\r\n const predictionsBatch = predictionsGridData.slice(\r\n i,\r\n batchEndIndex + 1\r\n );\r\n const userFilterResult: UserFilterResult = new UserFilterResult(\r\n batchGuid,\r\n accountId,\r\n accountName,\r\n predictionsBatch,\r\n summaryModel,\r\n activeStrategy,\r\n predictionType,\r\n durationInDays,\r\n goalText\r\n );\r\n await PredictionService.PredictionBatchUpload(userFilterResult);\r\n }\r\n\r\n const createExcelFromBatchRequest = new CreateExcelFromBatchRequest(\r\n batchGuid\r\n );\r\n const createPredictionsExcelResponse =\r\n await PredictionService.CreateExcelFromBatch(\r\n createExcelFromBatchRequest\r\n );\r\n\r\n const creationDate = new Date(\r\n createPredictionsExcelResponse.creationDate\r\n );\r\n\r\n const fileName = `${accountName}-PPR-${creationDate.getUTCFullYear()}-${\r\n creationDate.getUTCMonth() + 1\r\n }-${creationDate.getUTCDate()}-${\r\n createPredictionsExcelResponse.marginPct\r\n }`;\r\n\r\n const excelBlob = (\r\n await axios.get(createPredictionsExcelResponse.excelUrl, {\r\n responseType: \"blob\",\r\n })\r\n ).data;\r\n\r\n new FileDownloader().DownloadFromBlob(excelBlob, `${fileName}.xlsx`);\r\n\r\n setIsExcelDownloadInProcess(false);\r\n };\r\n\r\n // const auditPredictions = async () => {\r\n // if (predictionStore.GetPredictionSummary.budget === \"0\") {\r\n // return;\r\n // }\r\n\r\n // const accountId = predictionStore.account!.accountId;\r\n // const predictionsGridData = predictionStore.GetGridData(\r\n // predictionStore.filteredPredictions\r\n // );\r\n // const summaryModel = predictionStore.GetPredictionSummary;\r\n // const activeStrategy =\r\n // predictionStore.activeCampaignStrategy as EStrategyName;\r\n // const predictionType =\r\n // predictionStore.userPredictionRequest!.predictionType;\r\n // const durationInDays =\r\n // predictionStore.predictionSettings.predictionDuration;\r\n\r\n // const auditRequest: AuditPredictionRequest = new AuditPredictionRequest(\r\n // accountId,\r\n // /* predictionsGridData */ [],\r\n // summaryModel,\r\n // activeStrategy,\r\n // predictionType,\r\n // durationInDays\r\n // );\r\n\r\n // await PredictionService.AuditPredictions(auditRequest);\r\n // };\r\n\r\n /* Render\r\n * ################################################\r\n */\r\n\r\n if (!predictionStore.GetPredictionSummary) {\r\n throw new Error(\"There is no prediction summary calculated\");\r\n }\r\n\r\n if (!predictionStore.predictionResult) {\r\n return ;\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n Predictive Performance Report:{\" \"}\r\n \r\n {predictionStore.account!.accountName}\r\n \r\n \r\n \r\n\r\n \r\n {isExcelDownloadInProcess ? (\r\n \r\n ) : (\r\n \r\n }\r\n onClick={async () => {\r\n try {\r\n const tasks = [];\r\n tasks.push(downloadExcelHandler());\r\n // tasks.push(auditPredictions());\r\n await Promise.all(tasks);\r\n } catch (error) {\r\n console.error(error);\r\n message.error(\"An error occured\");\r\n }\r\n }}>\r\n Download\r\n \r\n )}\r\n\r\n }>\r\n Edit Goal\r\n \r\n \r\n \r\n\r\n \r\n \r\n\r\n \r\n\r\n \r\n\r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n\r\n {/* Charts Section */}\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default withRouter(observer(PredictionPage));\r\n","export default \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAATCAYAAABPwleqAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAD6ADAAQAAAABAAAAEwAAAACiJqGKAAACGElEQVQ4EY2TT2gTQRTG35tZa8VS6EGRQhW6rZ78Q0FKqYLiQQ8ieumhifWkB6mCqJhNDQxoiHhoDxUFLyJke1DQiz2oNz2LetK2NoKXVLGgqKEh2Xl9b+mi2U2kD7Iz33vvt5n5ZhYPGeN8mV+5jIhHgUBB06BvCvSdhZn8q3/L6KYmThEFTxDwNwEFDUVAzbmOMIdoAWG8VCzci3ocBveI0Mo5MF+88T4qyLgzndtbt7V3vKqHALSLLNx1U173ol/ISb3FMqX0N4joc09/+0FElSeArJvK3JbqumDuM+xLjchOAJEigisCO/JoFbZry0dcLj8npO6oh73ZwSvpFP1f+NP0xSr3HItAGd1R7ykPJ2WegPvOXHdt3Q7C5s7Z0v3Mz74xb4gCtW3RzwvUEIk9U2CHgayPf37kdo9muqgOL/hEJhuoNZGAB07s8xHhAwCerSB4cs4KVXg08Rck4McjIwEqfU1MYVevIsDbdH/bTBwUnYAl2aE3vGZIzOIF4EtjjA3nsUdT+FdtJcuXYSODZb7v593TZmuMC2UC7h0z2wnwAv/zGw1wTvZMQdWsCwZbPcKNZTYtu+AXnvHHMMt6uBmcOGf+ah5wo/zCKPm3jkfz+OigUhWyFgKop3tT3uF4Q1zzNlx2sSJ5p83iI7bVI0vhZY83N9WKpiTPvgAMXZrc9P3r8n7SNtSSaxWa9NJc8eac1FcB3EC/WajJgc4AAAAASUVORK5CYII=\"","export default __webpack_public_path__ + \"static/media/ppr-admin-background.1479a55f.jpg\";","import { makeStyles } from \"@material-ui/core\";\r\nimport pprBg from \"../../assets/images/ppr-admin-background.jpg\";\r\n\r\nexport const useStyles = makeStyles((theme) => ({\r\n background: {\r\n backgroundImage: `url(${pprBg})`,\r\n backgroundRepeat: \"no-repeat\",\r\n backgroundSize: \"cover\",\r\n backgroundPosition: '0% 55%',\r\n width: \"100%\",\r\n height: \"calc(100vh - 80px)\",\r\n },\r\n paperWrapper: {\r\n position: \"relative\",\r\n height: \"438px\",\r\n width: \"641px\",\r\n maxWidth: \"70%\",\r\n maxHeight: \"70%\",\r\n paddingLeft: theme.spacing(14),\r\n paddingTop: theme.spacing(14),\r\n },\r\n paper: {\r\n padding: theme.spacing(12),\r\n width: \"100%\",\r\n minHeight: \"100%\",\r\n },\r\n pprTitle: {\r\n color: \"#485465\",\r\n fontFamily: \"Open Sans\",\r\n fontSize: \"20px\",\r\n fontWeight: 800,\r\n letterSpacing: 0,\r\n lineHeight: \"27px\",\r\n },\r\n subTitle: {\r\n color: \"#485465\",\r\n fontFamily: \"Open Sans\",\r\n fontSize: \"16px\",\r\n lineHeight: \"22px\",\r\n marginBottom: theme.spacing(1),\r\n },\r\n or: {\r\n height: \"22px\",\r\n color: \"#999B9D\",\r\n fontFamily: \"Open Sans\",\r\n fontSize: \"16px\",\r\n lineHeight: \"22px\",\r\n },\r\n clearButton: {\r\n color: \"#3992B3\",\r\n fontFamily: \"Open Sans\",\r\n fontSize: \"14px\",\r\n lineHeight: \"19px\",\r\n textTransform: 'none'\r\n },\r\n}));\r\n","export class PreloadRequest {\r\n /**\r\n *\r\n */\r\n constructor(\r\n public accountId: number,\r\n public margin: number,\r\n public cvh: number,\r\n public userId: number,\r\n public areExpansionsExcluded: boolean\r\n ) {}\r\n}\r\n","import { ServiceUrls } from \"../../constants/ServiceUrls\";\r\nimport { BaseService } from \"../BaseService\";\r\nimport { Account } from '../../models/Account';\r\nimport { ApiRoutes } from './../../constants/ApiRoutes';\r\n\r\nexport class AccountService extends BaseService {\r\n public async GetAllAccounts(): Promise {\r\n const accounts = await this.get(ApiRoutes.Account.GetAllAccounts);\r\n return accounts;\r\n }\r\n \r\n}\r\n\r\nexport default new AccountService(ServiceUrls.Account);","import { JSXElementConstructor, ReactElement } from \"react\";\r\nimport {\r\n Box,\r\n Button,\r\n Checkbox,\r\n FormControlLabel,\r\n FormGroup,\r\n} from \"@material-ui/core\";\r\nimport { Account } from \"../../models/Account\";\r\nimport Loader from \"../common/feedback/loader/Loader\";\r\nimport AutoCompleteInput from \"../common/inputs/auto-complete-input/AutoCompleteInput\";\r\nimport Input from \"../common/inputs/input/Input\";\r\n\r\nexport class AdminPreloadPageBuilder {\r\n /**\r\n *\r\n */\r\n constructor(public classes: any) {}\r\n\r\n public Title(title: string) {\r\n return {title} ;\r\n }\r\n\r\n public AccountAutocompleteInput(\r\n accountList: Account[],\r\n account: Account | null,\r\n setAccount: (account: Account | null) => void,\r\n handleSubmit: () => void\r\n ) {\r\n return (\r\n \r\n Select account \r\n {accountList.length === 0 ? (\r\n \r\n \r\n \r\n ) : (\r\n v?.accountName ?? \"\"}\r\n onChange={(v) => setAccount(v)}\r\n onEnterPress={handleSubmit}\r\n filterOptions={(accounts, state) =>\r\n accounts.filter(\r\n (account) =>\r\n account?.accountName\r\n .toLowerCase()\r\n .includes(\r\n state.inputValue.toLowerCase()\r\n ) ||\r\n account?.accountId.toString() ===\r\n state.inputValue\r\n )\r\n }\r\n value={account}\r\n />\r\n )}\r\n \r\n );\r\n }\r\n\r\n public Input(\r\n title: string,\r\n margin: string,\r\n setMargin: (value: string) => void,\r\n handleSubmit: () => void\r\n ) {\r\n return (\r\n \r\n {title} \r\n \r\n \r\n );\r\n }\r\n\r\n public CheckBox(\r\n label: string,\r\n areExpansionsExcluded: boolean,\r\n setAreExpansionsExcluded: (state: boolean) => void\r\n ) {\r\n return (\r\n \r\n {\r\n setAreExpansionsExcluded(event.target.checked);\r\n }}\r\n color=\"primary\"\r\n />\r\n }\r\n label={label}\r\n />\r\n \r\n );\r\n }\r\n\r\n public Buttons(\r\n loading: boolean,\r\n handleSubmit: () => void,\r\n handleClear: () => void\r\n ) {\r\n if (loading) {\r\n return (\r\n \r\n \r\n \r\n );\r\n }\r\n return (\r\n <>\r\n \r\n \r\n Save changes\r\n \r\n \r\n\r\n \r\n \r\n Clear form\r\n \r\n \r\n >\r\n );\r\n }\r\n}\r\n","export type severity = \"error\" | \"warning\" | \"info\";\r\n\r\nexport class ValidatorResponse {\r\n /**\r\n *\r\n */\r\n constructor(\r\n public isValid: boolean,\r\n public severity: severity,\r\n public errorMessage: string\r\n ) {\r\n\r\n }\r\n}\r\n\r\nexport interface IInputValidator {\r\n isValid(requestParams: T): ValidatorResponse;\r\n notifyError(validatorResponse: ValidatorResponse): void;\r\n}\r\n\r\n","import { Account } from '../../models/Account';\r\nimport { BaseInputValidator } from '../../services/input-validator/BaseInputValidator';\r\nimport { NumbersHelper } from '../../services/helpers/NumbersHelper';\r\nimport { IInputValidator, severity, ValidatorResponse } from '../../services/input-validator/IInputValidator';\r\n\r\nexport interface InputType {\r\n account: Account | null | undefined;\r\n margin?: string;\r\n cvh?: string;\r\n}\r\n\r\nexport class AdminPreloadRule\r\n extends BaseInputValidator\r\n implements IInputValidator {\r\n\r\n isValid(requestParams: InputType): ValidatorResponse {\r\n if (!requestParams.account) {\r\n return new ValidatorResponse(false, \"error\", \"Please select an account\");\r\n }\r\n\r\n if (!requestParams.margin) {\r\n return new ValidatorResponse(false, \"error\", \"Please select a margin\");\r\n }\r\n\r\n if (!requestParams.cvh) {\r\n return new ValidatorResponse(false, \"error\", \"Please enter applicants to hire conversion rate\");\r\n }\r\n\r\n if (NumbersHelper.isNumber(requestParams.cvh) === false) {\r\n return new ValidatorResponse(false, \"error\", \"Please make sure applicants to hire conversion is a valid number\");\r\n }\r\n\r\n if (\r\n NumbersHelper.isNumber(requestParams.margin) === false ||\r\n Number(requestParams.margin) < 0 ||\r\n Number(requestParams.margin) > 100\r\n ) {\r\n return new ValidatorResponse(false, \"error\", \"Please make sure margin is a number between 0 and 100\");\r\n }\r\n\r\n\r\n\r\n return new ValidatorResponse(true, \"info\", \"\");\r\n }\r\n}","import { message } from 'antd';\r\nimport { IInputValidator, severity, ValidatorResponse } from './IInputValidator';\r\n\r\n\r\nexport abstract class BaseInputValidator implements IInputValidator {\r\n abstract isValid(requestParams: T): ValidatorResponse;\r\n\r\n notifyError(validatorResponse: ValidatorResponse): void {\r\n // tightly coupled to ant design\r\n message[validatorResponse.severity](validatorResponse.errorMessage);\r\n }\r\n}\r\n","import { Box, Paper } from \"@material-ui/core\";\r\nimport { ReactElement, useContext, useState } from \"react\";\r\nimport { useStyles } from \"./AdminPreloadPage.styles\";\r\nimport { message } from \"antd\";\r\nimport useSetCurrentPage from \"../../hooks/useSetCurrentPage\";\r\nimport { PreloadRequest } from \"../../models/prediction/PreloadRequest\";\r\nimport { Account } from \"../../models/Account\";\r\nimport useOnLoadAsync from \"../../hooks/useOnLoadAsync\";\r\nimport AccountService from \"../../services/account/AccountService\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport { RootStoreContext } from \"../../stores/RootStoreContext\";\r\nimport { ErrorMessages } from \"../../constants/ErrorMessages\";\r\nimport { AdminPreloadPageBuilder } from \"./AdminPreloadPage.builder\";\r\nimport useKeyboardListener from \"../../hooks/useKeyboardListener\";\r\nimport { Guid } from \"../../services/Guid\";\r\nimport { RouteComponentProps } from \"react-router-dom\";\r\nimport { Routes } from \"../../constants/Routes\";\r\nimport { withRouter } from \"react-router\";\r\nimport { AdminPreloadRule, InputType } from \"./AdminPreloadRule\";\r\nimport { IInputValidator } from \"../../services/input-validator/IInputValidator\";\r\nimport { EGrantType } from \"../../models/users/User\";\r\n\r\ninterface Props extends RouteComponentProps {}\r\n\r\nconst inputDefaultValues = {\r\n margin: \"50\",\r\n cvh: \"5\",\r\n};\r\n\r\nfunction AdminPreloadPage(props: Props): ReactElement {\r\n const { history } = props;\r\n const { userStore, predictionStore } = useContext(RootStoreContext);\r\n const [margin, setMargin] = useState(inputDefaultValues.margin);\r\n const [cvh, setCvh] = useState(inputDefaultValues.cvh);\r\n const [loading, setLoading] = useState(false);\r\n const [account, setAccount] = useState(null);\r\n const [accountList, setAccountList] = useState([]);\r\n const [areExpansionsExcluded, setAreExpansionsExcluded] = useState(true);\r\n const classes = useStyles();\r\n\r\n const handleSubmit = async () => {\r\n const ruleValidator: IInputValidator =\r\n new AdminPreloadRule();\r\n const validatorResponse = ruleValidator.isValid({\r\n account,\r\n margin,\r\n cvh,\r\n });\r\n if (!validatorResponse.isValid) {\r\n ruleValidator.notifyError(validatorResponse);\r\n return;\r\n }\r\n\r\n const preloadRequest: PreloadRequest = new PreloadRequest(\r\n account!.accountId,\r\n Number(margin),\r\n Number(cvh),\r\n userStore.user!.userId,\r\n areExpansionsExcluded\r\n );\r\n\r\n try {\r\n setLoading(true);\r\n const result = await predictionStore.PreloadAccount(preloadRequest);\r\n message.success(result);\r\n history.push(Routes.InputPage);\r\n } catch (error: any) {\r\n message.error(error.message);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const handleClear = () => {\r\n setAccount(null);\r\n setMargin(inputDefaultValues.margin);\r\n setCvh(inputDefaultValues.cvh);\r\n };\r\n\r\n /* Hooks\r\n * ################################################\r\n */\r\n\r\n useSetCurrentPage();\r\n\r\n useOnLoadAsync(async () => {\r\n const accounts = await userStore.MakeAuthorizedHttpRequest(async () => {\r\n return await AccountService.GetAllAccounts();\r\n });\r\n if (!accounts) {\r\n message.error(ErrorMessages.TokenExpired);\r\n return;\r\n }\r\n setAccountList(accounts);\r\n });\r\n\r\n useKeyboardListener(Guid.NewGuid(), [\"alt\", \"1\"], () => {\r\n history.push(Routes.InputPage);\r\n });\r\n\r\n const builder = new AdminPreloadPageBuilder(classes);\r\n return (\r\n \r\n \r\n \r\n \r\n {/* {builder.Title(\"Admin Page - Preparation\")} */}\r\n {builder.AccountAutocompleteInput(\r\n accountList,\r\n account,\r\n setAccount,\r\n handleSubmit\r\n )}\r\n {userStore.user!.eGrantType === EGrantType.Admin &&\r\n builder.Input(\r\n \"Margin (%)\",\r\n margin,\r\n setMargin,\r\n handleSubmit\r\n )}\r\n\r\n {builder.Input(\r\n \"Applicants to hire conversion rate (%)\",\r\n cvh,\r\n setCvh,\r\n handleSubmit\r\n )}\r\n\r\n {builder.CheckBox(\r\n \"Exclude existing job expansion variants from report\",\r\n areExpansionsExcluded,\r\n setAreExpansionsExcluded\r\n )}\r\n\r\n {builder.Buttons(loading, handleSubmit, handleClear)}\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default withRouter(observer(AdminPreloadPage));\r\n","\r\nimport React from 'react';\r\nimport { Routes } from '../constants/Routes';\r\nimport LoginPage from './../components/login-page/LoginPage';\r\nimport InputPage from './../components/input-page/InputPage';\r\nimport PredictionPage from './../components/predictions-page/PredictionPage';\r\nimport AdminPreloadPage from '../components/admin-preload-page/AdminPreloadPage';\r\n\r\nexport class RouteConfiguration {\r\n /**\r\n *\r\n */\r\n constructor(\r\n public path: string,\r\n public reactComponent: React.ComponentClass,\r\n public isAuthorizedRoute: boolean\r\n ) { }\r\n}\r\n\r\nexport class RouteSetup {\r\n public static GetRouteConfiguration() {\r\n return [\r\n new RouteConfiguration(Routes.Login, LoginPage, false),\r\n new RouteConfiguration(Routes.InputPage, InputPage, true),\r\n new RouteConfiguration(Routes.PredictionPage, PredictionPage, true),\r\n new RouteConfiguration(Routes.AdminPreload, AdminPreloadPage, true),\r\n ]\r\n }\r\n}","import { Box, Slide, withStyles } from \"@material-ui/core\";\r\nimport React from \"react\";\r\nimport { BrowserRouter as Router, Route, Switch } from \"react-router-dom\";\r\nimport { Routes } from \"./constants/Routes\";\r\nimport Header from \"./components/common/header/Header\";\r\nimport PredictionPage from \"./components/predictions-page/PredictionPage\";\r\nimport { observer } from \"mobx-react-lite\";\r\nimport Footer from \"./components/common/footer/Footer\";\r\nimport InputPage from \"./components/input-page/InputPage\";\r\nimport { getEnvironment } from \"./config/EnvironmentUrls\";\r\nimport LoginPage from \"./components/login-page/LoginPage\";\r\nimport AuthorizedRoute from \"./hoc/AuthorizedRoute\";\r\nimport useKeyboardListener from \"./hooks/useKeyboardListener\";\r\nimport { Guid } from \"./services/Guid\";\r\nimport { RouteSetup } from \"./config/RouteSetup\";\r\nimport { SnackbarProvider, SnackbarProviderProps } from \"notistack\";\r\nimport { makeStyles } from '@material-ui/core/styles';\r\n\r\nconst SnackBarProviderCustomTheme = withStyles({\r\n variantInfo: {\r\n backgroundColor: 'yellow' \r\n }\r\n})(SnackbarProvider);\r\n\r\nconst styles = {\r\n // success: { backgroundColor: 'purple' },\r\n // error: { backgroundColor: 'blue' },\r\n // warning: { backgroundColor: 'green' },\r\n info: { backgroundColor: '#242C37 !important' }\r\n};\r\n\r\nfunction App() {\r\n useKeyboardListener(Guid.NewGuid(), \"nadar\".split(\"\"), () => {\r\n window.alert(\"Give kudos to Nadar for this amazing project ;)\");\r\n });\r\n\r\n const classes = makeStyles(styles)();\r\n\r\n const ApplicationRoutes = () => {\r\n return (\r\n \r\n {RouteSetup.GetRouteConfiguration().map(\r\n (routeConfiguration) => {\r\n if (routeConfiguration.isAuthorizedRoute) {\r\n return (\r\n \r\n \r\n \r\n );\r\n }\r\n return (\r\n \r\n \r\n \r\n );\r\n }\r\n )}\r\n \r\n );\r\n };\r\n\r\n \r\n\r\n return (\r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n );\r\n}\r\n\r\nexport default observer(App);\r\n","import { ReportHandler } from 'web-vitals';\r\n\r\nconst reportWebVitals = (onPerfEntry?: ReportHandler) => {\r\n if (onPerfEntry && onPerfEntry instanceof Function) {\r\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\r\n getCLS(onPerfEntry);\r\n getFID(onPerfEntry);\r\n getFCP(onPerfEntry);\r\n getLCP(onPerfEntry);\r\n getTTFB(onPerfEntry);\r\n });\r\n }\r\n};\r\n\r\nexport default reportWebVitals;\r\n","import { createMuiTheme } from \"@material-ui/core\";\r\n\r\nexport const MuiLightTheme = createMuiTheme({\r\n palette: {\r\n background: {\r\n default: \"#FFF\"\r\n },\r\n\r\n primary: {\r\n main: \"#225a78\"\r\n },\r\n secondary: {\r\n main: \"#77992e\"\r\n }\r\n },\r\n typography: {\r\n fontFamily: \"Open Sans\"\r\n },\r\n spacing: 5\r\n});","import { CssBaseline, ThemeProvider } from \"@material-ui/core\";\r\nimport React from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport App from \"./App\";\r\nimport reportWebVitals from \"./reportWebVitals\";\r\nimport { MuiLightTheme } from \"./config/MuiTheme\";\r\n\r\n/**\r\n * CSS imports\r\n */\r\nimport \"antd/dist/antd.css\"; // or 'antd/dist/antd.less'\r\nimport \"./assets/styles/reset.css\";\r\nimport \"./assets/styles/generic.scss\";\r\nimport { RootStoreProvider } from \"./stores/RootStoreContext\";\r\n\r\n/**\r\n * Licenses\r\n */\r\nimport {LicenseManager} from \"@ag-grid-enterprise/core\";\r\nLicenseManager.setLicenseKey(process.env.REACT_APP_AG_GRID_TOKEN!);\r\n\r\nReactDOM.render(\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ,\r\n document.getElementById(\"root\")\r\n);\r\n\r\n// If you want to start measuring performance in your app, pass a function\r\n// to log results (for example: reportWebVitals(console.log))\r\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\r\nreportWebVitals();\r\n"],"sourceRoot":""}