Extract bet amount input to component.

This commit is contained in:
jahooma 2022-01-10 21:41:42 -06:00
parent 6ef48af085
commit 7fbecbc102
4 changed files with 107 additions and 87 deletions

View File

@ -18,7 +18,7 @@ export function AddFundsButton(props: { className?: string }) {
<label <label
htmlFor="add-funds" htmlFor="add-funds"
className={clsx( className={clsx(
'btn btn-sm normal-case modal-button bg-gradient-to-r from-teal-500 to-green-500 hover:from-teal-600 hover:to-green-600 font-normal border-none', 'btn btn-xs normal-case modal-button bg-gradient-to-r from-teal-500 to-green-500 hover:from-teal-600 hover:to-green-600 font-normal border-none',
className className
)} )}
> >

View File

@ -0,0 +1,79 @@
import clsx from 'clsx'
import { useUser } from '../hooks/use-user'
import { formatMoney } from '../lib/util/format'
import { AddFundsButton } from './add-funds-button'
import { Col } from './layout/col'
import { Row } from './layout/row'
export function AmountInput(props: {
amount: number | undefined
onChange: (newAmount: number | undefined) => void
error: string | undefined
setError: (error: string | undefined) => void
disabled?: boolean
className?: string
inputClassName?: string
}) {
const {
amount,
onChange,
error,
setError,
disabled,
className,
inputClassName,
} = props
const user = useUser()
const onAmountChange = (str: string) => {
const amount = parseInt(str.replace(/[^\d]/, ''))
if (str && isNaN(amount)) return
onChange(str ? amount : undefined)
if (user && user.balance < amount) setError('Insufficient balance')
else setError(undefined)
}
const remainingBalance = (user?.balance ?? 0) - (amount ?? 0)
return (
<Col className={className}>
<label className="input-group">
<span className="text-sm bg-gray-200">M$</span>
<input
className={clsx(
'input input-bordered',
error && 'input-error',
inputClassName
)}
type="text"
placeholder="0"
maxLength={9}
value={amount ?? ''}
disabled={disabled}
onChange={(e) => onAmountChange(e.target.value)}
/>
</label>
{user && (
<Row className="text-sm text-gray-500 justify-between mt-3 gap-4 items-end">
{error ? (
<div className="font-medium tracking-wide text-red-500 text-xs whitespace-nowrap mr-auto self-center">
{error}
</div>
) : (
<Col>
<div className="whitespace-nowrap">Remaining balance</div>
<div className="text-neutral mt-1">
{formatMoney(Math.floor(remainingBalance))}
</div>
</Col>
)}
{user.balance !== 1000 && <AddFundsButton className="mt-1" />}
</Row>
)}
</Col>
)
}

View File

@ -20,11 +20,11 @@ import {
calculatePayoutAfterCorrectBet, calculatePayoutAfterCorrectBet,
} from '../../common/calculate' } from '../../common/calculate'
import { firebaseLogin } from '../lib/firebase/users' import { firebaseLogin } from '../lib/firebase/users'
import { AddFundsButton } from './add-funds-button'
import { OutcomeLabel } from './outcome-label' import { OutcomeLabel } from './outcome-label'
import { AdvancedPanel } from './advanced-panel' import { AdvancedPanel } from './advanced-panel'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'
import { placeBet } from '../lib/firebase/api-call' import { placeBet } from '../lib/firebase/api-call'
import { AmountInput } from './amount-input'
export function BetPanel(props: { contract: Contract; className?: string }) { export function BetPanel(props: { contract: Contract; className?: string }) {
useEffect(() => { useEffect(() => {
@ -48,17 +48,9 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
setWasSubmitted(false) setWasSubmitted(false)
} }
function onBetChange(str: string) { function onBetChange(newAmount: number | undefined) {
setWasSubmitted(false) setWasSubmitted(false)
setBetAmount(newAmount)
const amount = parseInt(str.replace(/[^\d]/, ''))
if (str && isNaN(amount)) return
setBetAmount(str ? amount : undefined)
if (user && user.balance < amount) setError('Insufficient balance')
else setError(undefined)
} }
async function submitBet() { async function submitBet() {
@ -106,8 +98,6 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
: 0 : 0
const estimatedReturnPercent = (estimatedReturn * 100).toFixed() + '%' const estimatedReturnPercent = (estimatedReturn * 100).toFixed() + '%'
const remainingBalance = (user?.balance || 0) - (betAmount || 0)
return ( return (
<Col <Col
className={clsx('bg-gray-100 shadow-md px-8 py-6 rounded-md', className)} className={clsx('bg-gray-100 shadow-md px-8 py-6 rounded-md', className)}
@ -121,41 +111,17 @@ export function BetPanel(props: { contract: Contract; className?: string }) {
onSelect={(choice) => onBetChoice(choice)} onSelect={(choice) => onBetChoice(choice)}
/> />
<div className="mt-3 mb-1 text-sm text-gray-500"> <div className="my-3 text-sm text-gray-500">Amount </div>
Amount{' '} <AmountInput
{user && ( inputClassName="w-full"
<span className="float-right"> amount={betAmount}
{formatMoney( onChange={onBetChange}
remainingBalance > 0 ? Math.floor(remainingBalance) : 0 error={error}
)}{' '} setError={setError}
left disabled={isSubmitting}
</span>
)}
</div>
<Col className="my-2">
<label className="input-group">
<span className="text-sm bg-gray-200">M$</span>
<input
className={clsx(
'input input-bordered w-full',
error && 'input-error'
)}
type="text"
placeholder="0"
maxLength={9}
value={betAmount ?? ''}
onChange={(e) => onBetChange(e.target.value)}
/> />
</label>
{error && ( <Spacer h={4} />
<div className="font-medium tracking-wide text-red-500 text-xs mt-3">
{error}
</div>
)}
{user && user.balance !== 1000 && (
<AddFundsButton className="self-end mt-3" />
)}
</Col>
<div className="mt-2 mb-1 text-sm text-gray-500">Implied probability</div> <div className="mt-2 mb-1 text-sm text-gray-500">Implied probability</div>
<Row> <Row>

View File

@ -10,10 +10,10 @@ import { Title } from '../components/title'
import { useUser } from '../hooks/use-user' import { useUser } from '../hooks/use-user'
import { Contract, path } from '../lib/firebase/contracts' import { Contract, path } from '../lib/firebase/contracts'
import { Page } from '../components/page' import { Page } from '../components/page'
import { formatMoney } from '../lib/util/format'
import { AdvancedPanel } from '../components/advanced-panel' import { AdvancedPanel } from '../components/advanced-panel'
import { createContract } from '../lib/firebase/api-call' import { createContract } from '../lib/firebase/api-call'
import { Row } from '../components/layout/row' import { Row } from '../components/layout/row'
import { AmountInput } from '../components/amount-input'
// Allow user to create a new contract // Allow user to create a new contract
export default function NewContract() { export default function NewContract() {
@ -33,7 +33,7 @@ export default function NewContract() {
const [description, setDescription] = useState('') const [description, setDescription] = useState('')
const [ante, setAnte] = useState<number | undefined>(0) const [ante, setAnte] = useState<number | undefined>(0)
const [anteError, setAnteError] = useState('') const [anteError, setAnteError] = useState<string | undefined>()
const [closeDate, setCloseDate] = useState('') const [closeDate, setCloseDate] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
@ -75,17 +75,6 @@ export default function NewContract() {
await router.push(path(result.contract as Contract)) await router.push(path(result.contract as Contract))
} }
function onAnteChange(str: string) {
const amount = parseInt(str.replace(/[^\d]/, ''))
if (str && isNaN(amount)) return
setAnte(str ? amount : undefined)
if (user && user.balance < amount) setAnteError('Insufficient balance')
else setAnteError('')
}
const descriptionPlaceholder = `e.g. This market will resolve to “Yes” if, by June 2, 2021, 11:59:59 PM ET, Paxlovid (also known under PF-07321332)...` const descriptionPlaceholder = `e.g. This market will resolve to “Yes” if, by June 2, 2021, 11:59:59 PM ET, Paxlovid (also known under PF-07321332)...`
if (!creator) return <></> if (!creator) return <></>
@ -166,33 +155,19 @@ export default function NewContract() {
<label className="label"> <label className="label">
<span className="mb-1">Subsidize your market</span> <span className="mb-1">Subsidize your market</span>
</label> </label>
<AmountInput
<label className="input-group"> className="items-start"
<span className="text-sm ">M$</span> amount={ante}
<input onChange={setAnte}
className={clsx( error={anteError}
'input input-bordered', setError={setAnteError}
anteError && 'input-error'
)}
type="text"
placeholder="0"
maxLength={9}
value={ante ?? ''}
disabled={isSubmitting} disabled={isSubmitting}
onChange={(e) => onAnteChange(e.target.value)}
/> />
</label>
<label>
<span className="label-text text-gray-500 ml-1">
Remaining balance:{' '}
{formatMoney(remainingBalance > 0 ? remainingBalance : 0)}
</span>
</label>
</div> </div>
<Spacer h={4} /> <Spacer h={4} />
<div className="form-control"> <div className="form-control items-start mb-1">
<label className="label"> <label className="label">
<span className="mb-1">Close date</span> <span className="mb-1">Close date</span>
</label> </label>
@ -208,8 +183,8 @@ export default function NewContract() {
</div> </div>
<label> <label>
<span className="label-text text-gray-500 ml-1"> <span className="label-text text-gray-500 ml-1">
No new trades will be allowed after{' '} No trades allowed after this date
{closeDate ? formattedCloseTime : 'this time'} {/* {closeDate ? formattedCloseTime : 'this date'} */}
</span> </span>
</label> </label>
</AdvancedPanel> </AdvancedPanel>