Inga/mobilebetting (#911)

* mobile binary betting
This commit is contained in:
ingawei 2022-09-26 19:28:54 -05:00 committed by GitHub
parent 2fe9fe593d
commit e17a59ae23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 305 additions and 185 deletions

View File

@ -5,7 +5,8 @@ import { formatMoney } from 'common/util/format'
import { Col } from './layout/col' import { Col } from './layout/col'
import { SiteLink } from './site-link' import { SiteLink } from './site-link'
import { ENV_CONFIG } from 'common/envs/constants' import { ENV_CONFIG } from 'common/envs/constants'
import { useIsMobile } from 'web/hooks/use-is-mobile' import { useWindowSize } from 'web/hooks/use-window-size'
import { Row } from './layout/row'
export function AmountInput(props: { export function AmountInput(props: {
amount: number | undefined amount: number | undefined
@ -34,15 +35,22 @@ export function AmountInput(props: {
const isInvalid = !str || isNaN(amount) const isInvalid = !str || isNaN(amount)
onChange(isInvalid ? undefined : amount) onChange(isInvalid ? undefined : amount)
} }
const isMobile = useIsMobile(768)
const { width } = useWindowSize()
const isMobile = (width ?? 0) < 768
return ( return (
<>
<Col className={className}> <Col className={className}>
<label className="input-group mb-4"> <label className="font-sm md:font-lg">
<span className="bg-gray-200 text-sm">{label}</span> <span className={clsx('text-greyscale-4 absolute ml-2 mt-[9px]')}>
{label}
</span>
<input <input
className={clsx( className={clsx(
'input input-bordered max-w-[200px] text-lg placeholder:text-gray-400', 'placeholder:text-greyscale-4 border-greyscale-2 rounded-md pl-9',
error && 'input-error', error && 'input-error',
isMobile ? 'w-24' : '',
inputClassName inputClassName
)} )}
ref={inputRef} ref={inputRef}
@ -59,7 +67,7 @@ export function AmountInput(props: {
</label> </label>
{error && ( {error && (
<div className="mb-2 mr-auto self-center whitespace-nowrap text-xs font-medium tracking-wide text-red-500"> <div className="absolute mt-11 whitespace-nowrap text-xs font-medium tracking-wide text-red-500">
{error === 'Insufficient balance' ? ( {error === 'Insufficient balance' ? (
<> <>
Not enough funds. Not enough funds.
@ -73,6 +81,7 @@ export function AmountInput(props: {
</div> </div>
)} )}
</Col> </Col>
</>
) )
} }
@ -135,6 +144,7 @@ export function BuyAmountInput(props: {
return ( return (
<> <>
<Row className="gap-4">
<AmountInput <AmountInput
amount={amount} amount={amount}
onChange={onAmountChange} onChange={onAmountChange}
@ -152,10 +162,11 @@ export function BuyAmountInput(props: {
max="205" max="205"
value={getRaw(amount ?? 0)} value={getRaw(amount ?? 0)}
onChange={(e) => onAmountChange(parseRaw(parseInt(e.target.value)))} onChange={(e) => onAmountChange(parseRaw(parseInt(e.target.value)))}
className="range range-lg only-thumb z-40 mb-2 xl:hidden" className="range range-lg only-thumb z-40 my-auto align-middle xl:hidden"
step="5" step="5"
/> />
)} )}
</Row>
</> </>
) )
} }

View File

@ -182,9 +182,9 @@ export function AnswerBetPanel(props: {
</Col> </Col>
<Spacer h={6} /> <Spacer h={6} />
{user ? ( {user ? (
<WarningConfirmationButton <WarningConfirmationButton
amount={betAmount}
warning={warning} warning={warning}
onSubmit={submitBet} onSubmit={submitBet}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}

View File

@ -1,8 +1,12 @@
import { useState } from 'react' import { useState } from 'react'
import clsx from 'clsx' import clsx from 'clsx'
import { SimpleBetPanel } from './bet-panel' import { BuyPanel, SimpleBetPanel } from './bet-panel'
import { CPMMBinaryContract, PseudoNumericContract } from 'common/contract' import {
BinaryContract,
CPMMBinaryContract,
PseudoNumericContract,
} from 'common/contract'
import { Modal } from './layout/modal' import { Modal } from './layout/modal'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { useUserContractBets } from 'web/hooks/use-user-bets' import { useUserContractBets } from 'web/hooks/use-user-bets'
@ -10,6 +14,9 @@ import { useSaveBinaryShares } from './use-save-binary-shares'
import { Col } from './layout/col' import { Col } from './layout/col'
import { Button } from 'web/components/button' import { Button } from 'web/components/button'
import { BetSignUpPrompt } from './sign-up-prompt' import { BetSignUpPrompt } from './sign-up-prompt'
import { User } from 'web/lib/firebase/users'
import { SellRow } from './sell-row'
import { useUnfilledBets } from 'web/hooks/use-bets'
/** Button that opens BetPanel in a new modal */ /** Button that opens BetPanel in a new modal */
export default function BetButton(props: { export default function BetButton(props: {
@ -64,7 +71,6 @@ export default function BetButton(props: {
<SimpleBetPanel <SimpleBetPanel
className={betPanelClassName} className={betPanelClassName}
contract={contract} contract={contract}
selected="YES"
onBetSuccess={() => setOpen(false)} onBetSuccess={() => setOpen(false)}
hasShares={hasYesShares || hasNoShares} hasShares={hasYesShares || hasNoShares}
/> />
@ -72,3 +78,44 @@ export default function BetButton(props: {
</> </>
) )
} }
export function BinaryMobileBetting(props: { contract: BinaryContract }) {
const { contract } = props
const user = useUser()
if (user) {
return <SignedInBinaryMobileBetting contract={contract} user={user} />
} else {
return <BetSignUpPrompt className="w-full" />
}
}
export function SignedInBinaryMobileBetting(props: {
contract: BinaryContract
user: User
}) {
const { contract, user } = props
const unfilledBets = useUnfilledBets(contract.id) ?? []
return (
<>
<Col className="w-full gap-2 px-1">
<Col>
<BuyPanel
hidden={false}
contract={contract as CPMMBinaryContract}
user={user}
unfilledBets={unfilledBets}
mobileView={true}
/>
</Col>
<SellRow
contract={contract}
user={user}
className={
'border-greyscale-3 bg-greyscale-1 rounded-md border-2 px-4 py-2'
}
/>
</Col>
</>
)
}

View File

@ -43,6 +43,11 @@ import { PlayMoneyDisclaimer } from './play-money-disclaimer'
import { isAndroid, isIOS } from 'web/lib/util/device' import { isAndroid, isIOS } from 'web/lib/util/device'
import { WarningConfirmationButton } from './warning-confirmation-button' import { WarningConfirmationButton } from './warning-confirmation-button'
import { MarketIntroPanel } from './market-intro-panel' import { MarketIntroPanel } from './market-intro-panel'
import { Modal } from './layout/modal'
import { Title } from './title'
import toast from 'react-hot-toast'
import { CheckIcon } from '@heroicons/react/solid'
import { useWindowSize } from 'web/hooks/use-window-size'
export function BetPanel(props: { export function BetPanel(props: {
contract: CPMMBinaryContract | PseudoNumericContract contract: CPMMBinaryContract | PseudoNumericContract
@ -105,11 +110,10 @@ export function BetPanel(props: {
export function SimpleBetPanel(props: { export function SimpleBetPanel(props: {
contract: CPMMBinaryContract | PseudoNumericContract contract: CPMMBinaryContract | PseudoNumericContract
className?: string className?: string
selected?: 'YES' | 'NO'
hasShares?: boolean hasShares?: boolean
onBetSuccess?: () => void onBetSuccess?: () => void
}) { }) {
const { contract, className, selected, hasShares, onBetSuccess } = props const { contract, className, hasShares, onBetSuccess } = props
const user = useUser() const user = useUser()
const [isLimitOrder, setIsLimitOrder] = useState(false) const [isLimitOrder, setIsLimitOrder] = useState(false)
@ -139,7 +143,6 @@ export function SimpleBetPanel(props: {
contract={contract} contract={contract}
user={user} user={user}
unfilledBets={unfilledBets} unfilledBets={unfilledBets}
selected={selected}
onBuySuccess={onBetSuccess} onBuySuccess={onBetSuccess}
/> />
<LimitOrderPanel <LimitOrderPanel
@ -162,38 +165,52 @@ export function SimpleBetPanel(props: {
) )
} }
function BuyPanel(props: { export function BuyPanel(props: {
contract: CPMMBinaryContract | PseudoNumericContract contract: CPMMBinaryContract | PseudoNumericContract
user: User | null | undefined user: User | null | undefined
unfilledBets: Bet[] unfilledBets: Bet[]
hidden: boolean hidden: boolean
selected?: 'YES' | 'NO'
onBuySuccess?: () => void onBuySuccess?: () => void
mobileView?: boolean
}) { }) {
const { contract, user, unfilledBets, hidden, selected, onBuySuccess } = props const { contract, user, unfilledBets, hidden, onBuySuccess, mobileView } =
props
const initialProb = getProbability(contract) const initialProb = getProbability(contract)
const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC' const isPseudoNumeric = contract.outcomeType === 'PSEUDO_NUMERIC'
const [outcome, setOutcome] = useState<'YES' | 'NO' | undefined>(selected) const windowSize = useWindowSize()
const [betAmount, setBetAmount] = useState<number | undefined>(undefined) const initialOutcome =
windowSize.width && windowSize.width >= 1280 ? 'YES' : undefined
const [outcome, setOutcome] = useState<'YES' | 'NO' | undefined>(
initialOutcome
)
const [betAmount, setBetAmount] = useState<number | undefined>(10)
const [error, setError] = useState<string | undefined>() const [error, setError] = useState<string | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
const [wasSubmitted, setWasSubmitted] = useState(false)
const [inputRef, focusAmountInput] = useFocus() const [inputRef, focusAmountInput] = useFocus()
function onBetChoice(choice: 'YES' | 'NO') { function onBetChoice(choice: 'YES' | 'NO') {
setOutcome(choice) setOutcome(choice)
setWasSubmitted(false)
if (!isIOS() && !isAndroid()) { if (!isIOS() && !isAndroid()) {
focusAmountInput() focusAmountInput()
} }
} }
function mobileOnBetChoice(choice: 'YES' | 'NO' | undefined) {
if (outcome === choice) {
setOutcome(undefined)
} else {
setOutcome(choice)
}
if (!isIOS() && !isAndroid()) {
focusAmountInput()
}
}
function onBetChange(newAmount: number | undefined) { function onBetChange(newAmount: number | undefined) {
setWasSubmitted(false)
setBetAmount(newAmount) setBetAmount(newAmount)
if (!outcome) { if (!outcome) {
setOutcome('YES') setOutcome('YES')
@ -214,9 +231,13 @@ function BuyPanel(props: {
.then((r) => { .then((r) => {
console.log('placed bet. Result:', r) console.log('placed bet. Result:', r)
setIsSubmitting(false) setIsSubmitting(false)
setWasSubmitted(true)
setBetAmount(undefined) setBetAmount(undefined)
if (onBuySuccess) onBuySuccess() if (onBuySuccess) onBuySuccess()
else {
toast('Trade submitted!', {
icon: <CheckIcon className={'text-primary h-5 w-5'} />,
})
}
}) })
.catch((e) => { .catch((e) => {
if (e instanceof APIError) { if (e instanceof APIError) {
@ -249,6 +270,7 @@ function BuyPanel(props: {
unfilledBets as LimitBet[] unfilledBets as LimitBet[]
) )
const [seeLimit, setSeeLimit] = useState(false)
const resultProb = getCpmmProbability(newPool, newP) const resultProb = getCpmmProbability(newPool, newP)
const probStayedSame = const probStayedSame =
formatPercent(resultProb) === formatPercent(initialProb) formatPercent(resultProb) === formatPercent(initialProb)
@ -281,22 +303,79 @@ function BuyPanel(props: {
return ( return (
<Col className={hidden ? 'hidden' : ''}> <Col className={hidden ? 'hidden' : ''}>
<div className="my-3 text-left text-sm text-gray-500">
{isPseudoNumeric ? 'Direction' : 'Outcome'}
</div>
<YesNoSelector <YesNoSelector
className="mb-4" className="mb-4"
btnClassName="flex-1" btnClassName="flex-1"
selected={outcome} selected={outcome}
onSelect={(choice) => onBetChoice(choice)} onSelect={(choice) => {
if (mobileView) {
mobileOnBetChoice(choice)
} else {
onBetChoice(choice)
}
}}
isPseudoNumeric={isPseudoNumeric} isPseudoNumeric={isPseudoNumeric}
/> />
<Row className="my-3 justify-between text-left text-sm text-gray-500"> <Col
Amount className={clsx(
<span className={'xl:hidden'}> mobileView
Balance: {formatMoney(user?.balance ?? 0)} ? outcome === 'NO'
? 'bg-red-25'
: outcome === 'YES'
? 'bg-teal-50'
: 'hidden'
: 'bg-white',
mobileView ? 'rounded-lg px-4 py-2' : 'px-0'
)}
>
<Row className="mt-3 w-full gap-3">
<Col className="w-1/2 text-sm">
<Col className="text-greyscale-4 flex-nowrap whitespace-nowrap text-xs">
<div>
{isPseudoNumeric ? (
'Max payout'
) : (
<>Payout if {outcome ?? 'YES'}</>
)}
</div>
</Col>
<div>
<span className="whitespace-nowrap text-xl">
{formatMoney(currentPayout)}
</span> </span>
<span className="text-greyscale-4 text-xs">
{' '}
+{currentReturnPercent}
</span>
</div>
</Col>
<Col className="w-1/2 text-sm">
<div className="text-greyscale-4 text-xs">
{isPseudoNumeric ? 'Estimated value' : 'New Probability'}
</div>
{probStayedSame ? (
<div className="text-xl">{format(initialProb)}</div>
) : (
<div className="text-xl">
{format(resultProb)}
<span className={clsx('text-greyscale-4 text-xs')}>
{isPseudoNumeric ? (
<></>
) : (
<>
{' '}
{outcome != 'NO' && '+'}
{format(resultProb - initialProb)}
</>
)}
</span>
</div>
)}
</Col>
</Row>
<Row className="text-greyscale-4 mt-4 mb-1 justify-between text-left text-xs">
Amount
</Row> </Row>
<BuyAmountInput <BuyAmountInput
@ -310,63 +389,46 @@ function BuyPanel(props: {
showSliderOnMobile showSliderOnMobile
/> />
<Col className="mt-3 w-full gap-3">
<Row className="items-center justify-between text-sm">
<div className="text-gray-500">
{isPseudoNumeric ? 'Estimated value' : 'Probability'}
</div>
{probStayedSame ? (
<div>{format(initialProb)}</div>
) : (
<div>
{format(initialProb)}
<span className="mx-2"></span>
{format(resultProb)}
</div>
)}
</Row>
<Row className="items-center justify-between gap-2 text-sm">
<Row className="flex-nowrap items-center gap-2 whitespace-nowrap text-gray-500">
<div>
{isPseudoNumeric ? (
'Max payout'
) : (
<>
Payout if <BinaryOutcomeLabel outcome={outcome ?? 'YES'} />
</>
)}
</div>
</Row>
<div>
<span className="mr-2 whitespace-nowrap">
{formatMoney(currentPayout)}
</span>
(+{currentReturnPercent})
</div>
</Row>
</Col>
<Spacer h={8} /> <Spacer h={8} />
{user && ( {user && (
<WarningConfirmationButton <WarningConfirmationButton
amount={betAmount}
outcome={outcome}
warning={warning} warning={warning}
onSubmit={submitBet} onSubmit={submitBet}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
disabled={!!betDisabled}
openModalButtonClass={clsx( openModalButtonClass={clsx(
'btn mb-2 flex-1', 'btn mb-2 flex-1',
betDisabled betDisabled
? 'btn-disabled' ? 'btn-disabled bg-greyscale-1'
: outcome === 'YES' : outcome === 'NO'
? 'btn-primary' ? 'border-none bg-red-400 hover:bg-red-500'
: 'border-none bg-red-400 hover:bg-red-500' : 'border-none bg-teal-500 hover:bg-teal-600'
)} )}
/> />
)} )}
<button
{wasSubmitted && <div className="mt-4">Trade submitted!</div>} className="text-greyscale-6 mx-auto select-none text-sm underline xl:hidden"
onClick={() => setSeeLimit(true)}
>
Advanced
</button>
<Modal
open={seeLimit}
setOpen={setSeeLimit}
position="center"
className="rounded-lg bg-white px-4 pb-8"
>
<Title text="Limit Order" />
<LimitOrderPanel
hidden={!seeLimit}
contract={contract}
user={user}
unfilledBets={unfilledBets}
/>
</Modal>
</Col>
</Col> </Col>
) )
} }
@ -389,7 +451,6 @@ function LimitOrderPanel(props: {
const betChoice = 'YES' const betChoice = 'YES'
const [error, setError] = useState<string | undefined>() const [error, setError] = useState<string | undefined>()
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
const [wasSubmitted, setWasSubmitted] = useState(false)
const rangeError = const rangeError =
lowLimitProb !== undefined && lowLimitProb !== undefined &&
@ -437,7 +498,6 @@ function LimitOrderPanel(props: {
const noAmount = shares * (1 - (noLimitProb ?? 0)) const noAmount = shares * (1 - (noLimitProb ?? 0))
function onBetChange(newAmount: number | undefined) { function onBetChange(newAmount: number | undefined) {
setWasSubmitted(false)
setBetAmount(newAmount) setBetAmount(newAmount)
} }
@ -482,7 +542,6 @@ function LimitOrderPanel(props: {
.then((r) => { .then((r) => {
console.log('placed bet. Result:', r) console.log('placed bet. Result:', r)
setIsSubmitting(false) setIsSubmitting(false)
setWasSubmitted(true)
setBetAmount(undefined) setBetAmount(undefined)
setLowLimitProb(undefined) setLowLimitProb(undefined)
setHighLimitProb(undefined) setHighLimitProb(undefined)
@ -718,8 +777,6 @@ function LimitOrderPanel(props: {
: `Submit order${hasTwoBets ? 's' : ''}`} : `Submit order${hasTwoBets ? 's' : ''}`}
</button> </button>
)} )}
{wasSubmitted && <div className="mt-4">Order submitted!</div>}
</Col> </Col>
) )
} }
@ -866,11 +923,7 @@ export function SellPanel(props: {
<> <>
<AmountInput <AmountInput
amount={ amount={
amount amount ? (Math.round(amount) === 0 ? 0 : Math.floor(amount)) : 0
? Math.round(amount) === 0
? 0
: Math.floor(amount)
: undefined
} }
onChange={onAmountChange} onChange={onAmountChange}
label="Qty" label="Qty"

View File

@ -13,17 +13,16 @@ import {
PseudoNumericResolutionOrExpectation, PseudoNumericResolutionOrExpectation,
} from './contract-card' } from './contract-card'
import { Bet } from 'common/bet' import { Bet } from 'common/bet'
import BetButton from '../bet-button' import BetButton, { BinaryMobileBetting } from '../bet-button'
import { AnswersGraph } from '../answers/answers-graph' import { AnswersGraph } from '../answers/answers-graph'
import { import {
Contract, Contract,
BinaryContract,
CPMMContract, CPMMContract,
CPMMBinaryContract,
FreeResponseContract, FreeResponseContract,
MultipleChoiceContract, MultipleChoiceContract,
NumericContract, NumericContract,
PseudoNumericContract, PseudoNumericContract,
BinaryContract,
} from 'common/contract' } from 'common/contract'
import { ContractDetails } from './contract-details' import { ContractDetails } from './contract-details'
import { NumericGraph } from './numeric-graph' import { NumericGraph } from './numeric-graph'
@ -78,19 +77,18 @@ const BinaryOverview = (props: { contract: BinaryContract; bets: Bet[] }) => {
<Row className="justify-between gap-4"> <Row className="justify-between gap-4">
<OverviewQuestion text={contract.question} /> <OverviewQuestion text={contract.question} />
<BinaryResolutionOrChance <BinaryResolutionOrChance
className="hidden items-end xl:flex" className="flex items-end"
contract={contract} contract={contract}
large large
/> />
</Row> </Row>
<Row className="items-center justify-between gap-4 xl:hidden">
<BinaryResolutionOrChance contract={contract} />
{tradingAllowed(contract) && (
<BetWidget contract={contract as CPMMBinaryContract} />
)}
</Row>
</Col> </Col>
<ContractProbGraph contract={contract} bets={[...bets].reverse()} /> <ContractProbGraph contract={contract} bets={[...bets].reverse()} />
<Row className="items-center justify-between gap-4 xl:hidden">
{tradingAllowed(contract) && (
<BinaryMobileBetting contract={contract} />
)}
</Row>
</Col> </Col>
) )
} }

View File

@ -344,7 +344,7 @@ export function getColor(contract: Contract) {
return ( return (
OUTCOME_TO_COLOR[resolution as resolution] ?? OUTCOME_TO_COLOR[resolution as resolution] ??
// If resolved to a FR answer, use 'primary' // If resolved to a FR answer, use 'primary'
'primary' 'teal-500'
) )
} }
@ -355,5 +355,5 @@ export function getColor(contract: Contract) {
} }
// TODO: Not sure why eg green-400 doesn't work here; try upgrading Tailwind // TODO: Not sure why eg green-400 doesn't work here; try upgrading Tailwind
return 'primary' return 'teal-500'
} }

View File

@ -106,7 +106,7 @@ export const OUTCOME_TO_COLOR = {
} }
export function YesLabel() { export function YesLabel() {
return <span className="text-primary">YES</span> return <span className="text-teal-500">YES</span>
} }
export function HigherLabel() { export function HigherLabel() {

View File

@ -4,8 +4,11 @@ import React from 'react'
import { Row } from './layout/row' import { Row } from './layout/row'
import { ConfirmationButton } from './confirmation-button' import { ConfirmationButton } from './confirmation-button'
import { ExclamationIcon } from '@heroicons/react/solid' import { ExclamationIcon } from '@heroicons/react/solid'
import { formatMoney } from 'common/util/format'
export function WarningConfirmationButton(props: { export function WarningConfirmationButton(props: {
amount: number | undefined
outcome?: 'YES' | 'NO' | undefined
warning?: string warning?: string
onSubmit: () => void onSubmit: () => void
disabled?: boolean disabled?: boolean
@ -14,12 +17,14 @@ export function WarningConfirmationButton(props: {
submitButtonClassName?: string submitButtonClassName?: string
}) { }) {
const { const {
amount,
onSubmit, onSubmit,
warning, warning,
disabled, disabled,
isSubmitting, isSubmitting,
openModalButtonClass, openModalButtonClass,
submitButtonClassName, submitButtonClassName,
outcome,
} = props } = props
if (!warning) { if (!warning) {
@ -28,12 +33,15 @@ export function WarningConfirmationButton(props: {
className={clsx( className={clsx(
openModalButtonClass, openModalButtonClass,
isSubmitting ? 'loading' : '', isSubmitting ? 'loading' : '',
disabled && 'btn-disabled' (disabled || !outcome) && 'btn-disabled bg-greyscale-2'
)} )}
onClick={onSubmit} onClick={onSubmit}
disabled={disabled}
> >
{isSubmitting ? 'Submitting...' : 'Submit'} {isSubmitting
? 'Submitting...'
: amount
? `Wager ${formatMoney(amount)}`
: 'Wager'}
</button> </button>
) )
} }
@ -45,7 +53,7 @@ export function WarningConfirmationButton(props: {
openModalButtonClass, openModalButtonClass,
isSubmitting && 'btn-disabled loading' isSubmitting && 'btn-disabled loading'
), ),
label: 'Submit', label: amount ? `Wager ${formatMoney(amount)}` : 'Wager',
}} }}
cancelBtn={{ cancelBtn={{
label: 'Cancel', label: 'Cancel',

View File

@ -35,10 +35,11 @@ export function YesNoSelector(props: {
<button <button
className={clsx( className={clsx(
commonClassNames, commonClassNames,
'hover:bg-primary-focus border-primary hover:border-primary-focus hover:text-white',
selected == 'YES' selected == 'YES'
? 'bg-primary text-white' ? 'border-teal-500 bg-teal-500 text-white'
: 'text-primary bg-white', : selected == 'NO'
? 'border-greyscale-3 text-greyscale-3 bg-white hover:border-teal-500 hover:text-teal-500'
: 'border-teal-500 bg-white text-teal-500 hover:bg-teal-50',
btnClassName btnClassName
)} )}
onClick={() => onSelect('YES')} onClick={() => onSelect('YES')}
@ -52,10 +53,11 @@ export function YesNoSelector(props: {
<button <button
className={clsx( className={clsx(
commonClassNames, commonClassNames,
'border-red-400 hover:border-red-500 hover:bg-red-500 hover:text-white',
selected == 'NO' selected == 'NO'
? 'bg-red-400 text-white' ? 'border-red-400 bg-red-400 text-white'
: 'bg-white text-red-400', : selected == 'YES'
? 'border-greyscale-3 text-greyscale-3 bg-white hover:border-red-400 hover:text-red-400'
: 'border-red-400 bg-white text-red-400 hover:bg-red-50',
btnClassName btnClassName
)} )}
onClick={() => onSelect('NO')} onClick={() => onSelect('NO')}

View File

@ -16,6 +16,7 @@ module.exports = {
), ),
extend: { extend: {
colors: { colors: {
'red-25': '#FDF7F6',
'greyscale-1': '#FBFBFF', 'greyscale-1': '#FBFBFF',
'greyscale-2': '#E7E7F4', 'greyscale-2': '#E7E7F4',
'greyscale-3': '#D8D8EB', 'greyscale-3': '#D8D8EB',