Move to tailwindui for create market page (#332)
* Move to tailwindui * Remove commented code * Prettier * Show custom prob toggle, limit to 5-95% * match left margin * Show prob, date, time, other ui changes
This commit is contained in:
parent
92df092ad3
commit
d69f4f9a0a
|
@ -1,36 +1,56 @@
|
|||
import { Row } from './layout/row'
|
||||
import { RadioGroup } from '@headlessui/react'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
|
||||
export function ChoicesToggleGroup(props: {
|
||||
currentChoice: number
|
||||
setChoice: (p: number) => void
|
||||
choices: number[]
|
||||
titles: string[]
|
||||
currentChoice: number | string
|
||||
choicesMap: { [key: string]: string | number }
|
||||
isSubmitting?: boolean
|
||||
setChoice: (p: number | string) => void
|
||||
className?: string
|
||||
children?: React.ReactNode
|
||||
}) {
|
||||
const { currentChoice, setChoice, titles, choices, isSubmitting } = props
|
||||
const baseButtonClassName = 'btn btn-outline btn-md sm:btn-md normal-case'
|
||||
const activeClasss =
|
||||
'bg-indigo-600 focus:bg-indigo-600 hover:bg-indigo-600 text-white'
|
||||
const {
|
||||
currentChoice,
|
||||
setChoice,
|
||||
isSubmitting,
|
||||
choicesMap,
|
||||
className,
|
||||
children,
|
||||
} = props
|
||||
return (
|
||||
<Row className={'mt-2 items-center gap-2'}>
|
||||
<div className={'btn-group justify-stretch'}>
|
||||
{choices.map((choice, i) => {
|
||||
return (
|
||||
<button
|
||||
key={choice + i + ''}
|
||||
<RadioGroup
|
||||
value={currentChoice.toString()}
|
||||
onChange={(str) => null}
|
||||
className="mt-2"
|
||||
>
|
||||
<div className={`grid grid-cols-12 gap-3`}>
|
||||
{Object.keys(choicesMap).map((choiceKey) => (
|
||||
<RadioGroup.Option
|
||||
key={choiceKey}
|
||||
value={choicesMap[choiceKey]}
|
||||
onClick={() => setChoice(choicesMap[choiceKey])}
|
||||
className={({ active }) =>
|
||||
clsx(
|
||||
active ? 'ring-2 ring-indigo-500 ring-offset-2' : '',
|
||||
currentChoice === choicesMap[choiceKey]
|
||||
? 'border-transparent bg-indigo-500 text-white hover:bg-indigo-600'
|
||||
: 'border-gray-200 bg-white text-gray-900 hover:bg-gray-50',
|
||||
'flex cursor-pointer items-center justify-center rounded-md border py-3 px-3 text-sm font-medium normal-case',
|
||||
"hover:ring-offset-2' hover:ring-2 hover:ring-indigo-500",
|
||||
className
|
||||
)
|
||||
}
|
||||
disabled={isSubmitting}
|
||||
className={clsx(
|
||||
baseButtonClassName,
|
||||
currentChoice === choice ? activeClasss : ''
|
||||
)}
|
||||
onClick={() => setChoice(choice)}
|
||||
>
|
||||
{titles[i]}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<RadioGroup.Label as="span">{choiceKey}</RadioGroup.Label>
|
||||
</RadioGroup.Option>
|
||||
))}
|
||||
{children}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -204,7 +204,7 @@ function EditableCloseDate(props: {
|
|||
|
||||
const [isEditingCloseTime, setIsEditingCloseTime] = useState(false)
|
||||
const [closeDate, setCloseDate] = useState(
|
||||
closeTime && dayjs(closeTime).format('YYYY-MM-DDT23:59')
|
||||
closeTime && dayjs(closeTime).format('YYYY-MM-DDTHH:mm')
|
||||
)
|
||||
|
||||
const isSameYear = dayjs(closeTime).isSame(dayjs(), 'year')
|
||||
|
|
|
@ -17,7 +17,6 @@ import { useHasCreatedContractToday } from 'web/hooks/use-has-created-contract-t
|
|||
import { removeUndefinedProps } from 'common/util/object'
|
||||
import { CATEGORIES } from 'common/categories'
|
||||
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
||||
import { CalculatorIcon } from '@heroicons/react/outline'
|
||||
|
||||
export default function Create() {
|
||||
const [question, setQuestion] = useState('')
|
||||
|
@ -68,8 +67,6 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
const [minString, setMinString] = useState('')
|
||||
const [maxString, setMaxString] = useState('')
|
||||
const [description, setDescription] = useState('')
|
||||
const [showCalendar, setShowCalendar] = useState(false)
|
||||
const [showNumInput, setShowNumInput] = useState(false)
|
||||
|
||||
const [category, setCategory] = useState<string>('')
|
||||
// const [tagText, setTagText] = useState<string>(tag ?? '')
|
||||
|
@ -88,21 +85,26 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
|
||||
// const [anteError, setAnteError] = useState<string | undefined>()
|
||||
// By default, close the market a week from today
|
||||
const weekFromToday = dayjs().add(7, 'day').format('YYYY-MM-DDT23:59')
|
||||
const weekFromToday = dayjs().add(7, 'day').format('YYYY-MM-DD')
|
||||
const [closeDate, setCloseDate] = useState<undefined | string>(weekFromToday)
|
||||
|
||||
const [closeHoursMinutes, setCloseHoursMinutes] = useState<string>('23:59')
|
||||
const [probErrorText, setProbErrorText] = useState('')
|
||||
const [marketInfoText, setMarketInfoText] = useState('')
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const closeTime = closeDate ? dayjs(closeDate).valueOf() : undefined
|
||||
const closeTime = closeDate
|
||||
? dayjs(`${closeDate}T${closeHoursMinutes}`).valueOf()
|
||||
: undefined
|
||||
|
||||
const balance = creator?.balance || 0
|
||||
|
||||
const min = minString ? parseFloat(minString) : undefined
|
||||
const max = maxString ? parseFloat(maxString) : undefined
|
||||
// get days from today until the end of this year:
|
||||
const daysLeftInTheYear = dayjs().endOf('year').diff(dayjs(), 'day')
|
||||
|
||||
const isValid =
|
||||
initialProb > 0 &&
|
||||
initialProb < 100 &&
|
||||
(outcomeType === 'BINARY' ? initialProb >= 5 && initialProb <= 95 : true) &&
|
||||
question.length > 0 &&
|
||||
ante !== undefined &&
|
||||
ante !== null &&
|
||||
|
@ -122,8 +124,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
max - min > 0.01))
|
||||
|
||||
function setCloseDateInDays(days: number) {
|
||||
setShowCalendar(days === 0)
|
||||
const newCloseDate = dayjs().add(days, 'day').format('YYYY-MM-DDT23:59')
|
||||
const newCloseDate = dayjs().add(days, 'day').format('YYYY-MM-DD')
|
||||
setCloseDate(newCloseDate)
|
||||
}
|
||||
|
||||
|
@ -163,90 +164,83 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
return (
|
||||
<div>
|
||||
<label className="label mt-1">
|
||||
<span className="my-1">Answer type</span>
|
||||
<span className="mt-1">Answer type</span>
|
||||
</label>
|
||||
<Row className="form-control gap-2">
|
||||
<label className="label cursor-pointer gap-2">
|
||||
<input
|
||||
className="radio"
|
||||
type="radio"
|
||||
name="opt"
|
||||
checked={outcomeType === 'BINARY'}
|
||||
value="BINARY"
|
||||
onChange={() => setOutcomeType('BINARY')}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<span className="label-text">Yes / No</span>
|
||||
</label>
|
||||
|
||||
<label className="label cursor-pointer gap-2">
|
||||
<input
|
||||
className="radio"
|
||||
type="radio"
|
||||
name="opt"
|
||||
checked={outcomeType === 'FREE_RESPONSE'}
|
||||
value="FREE_RESPONSE"
|
||||
onChange={() => setOutcomeType('FREE_RESPONSE')}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
<span className="label-text">Free response</span>
|
||||
</label>
|
||||
<label className="label cursor-pointer gap-2">
|
||||
<input
|
||||
className="radio"
|
||||
type="radio"
|
||||
name="opt"
|
||||
checked={outcomeType === 'NUMERIC'}
|
||||
value="NUMERIC"
|
||||
onChange={() => setOutcomeType('NUMERIC')}
|
||||
/>
|
||||
<span className="label-text">Numeric (experimental)</span>
|
||||
</label>
|
||||
</Row>
|
||||
<ChoicesToggleGroup
|
||||
currentChoice={outcomeType}
|
||||
setChoice={(choice) => {
|
||||
if (choice === 'NUMERIC')
|
||||
setMarketInfoText(
|
||||
'Numeric markets are still experimental and subject to major revisions.'
|
||||
)
|
||||
else if (choice === 'FREE_RESPONSE')
|
||||
setMarketInfoText(
|
||||
'Users can submit their own answers to this market.'
|
||||
)
|
||||
else setMarketInfoText('')
|
||||
setOutcomeType(choice as outcomeType)
|
||||
}}
|
||||
choicesMap={{
|
||||
'Yes / No': 'BINARY',
|
||||
'Free response': 'FREE_RESPONSE',
|
||||
Numeric: 'NUMERIC',
|
||||
}}
|
||||
isSubmitting={isSubmitting}
|
||||
className={'col-span-4'}
|
||||
/>
|
||||
{marketInfoText && (
|
||||
<div className="mt-2 ml-1 text-sm text-indigo-700">
|
||||
{marketInfoText}
|
||||
</div>
|
||||
)}
|
||||
<Spacer h={4} />
|
||||
|
||||
{outcomeType === 'BINARY' && (
|
||||
<div className="form-control">
|
||||
<Row className="label justify-start">
|
||||
<span className="mb-1">How likely is it to happen?</span>
|
||||
<CalculatorIcon
|
||||
className={clsx(
|
||||
'ml-2 cursor-pointer rounded-md',
|
||||
'hover:bg-gray-200',
|
||||
showNumInput && 'stroke-indigo-700'
|
||||
)}
|
||||
height={20}
|
||||
onClick={() => setShowNumInput(!showNumInput)}
|
||||
/>
|
||||
</Row>
|
||||
<Row className={'w-full items-center sm:gap-2'}>
|
||||
<Row className={'justify-start'}>
|
||||
<ChoicesToggleGroup
|
||||
currentChoice={initialProb}
|
||||
setChoice={setInitialProb}
|
||||
choices={[25, 50, 75]}
|
||||
titles={['Unlikely', 'Unsure', 'Likely']}
|
||||
setChoice={(option) => {
|
||||
setProbErrorText('')
|
||||
setInitialProb(option as number)
|
||||
}}
|
||||
choicesMap={{
|
||||
Unlikely: 25,
|
||||
'Not Sure': 50,
|
||||
Likely: 75,
|
||||
}}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
{showNumInput && (
|
||||
<>
|
||||
className={'col-span-4 sm:col-span-3'}
|
||||
>
|
||||
<div className={'col-span-6 sm:col-span-3'}>
|
||||
<input
|
||||
type="number"
|
||||
value={initialProb}
|
||||
className={
|
||||
'max-w-[16%] sm:max-w-[15%] ' +
|
||||
'input-bordered input-md mt-2 rounded-md p-1 text-lg sm:p-4'
|
||||
'input-bordered input-md rounded-md border-gray-300 pr-2 text-lg'
|
||||
}
|
||||
min={5}
|
||||
max={95}
|
||||
disabled={isSubmitting}
|
||||
min={10}
|
||||
max={90}
|
||||
onChange={(e) =>
|
||||
setInitialProb(parseInt(e.target.value.substring(0, 2)))
|
||||
}
|
||||
onChange={(e) => {
|
||||
// show error if prob is less than 5 or greater than 95:
|
||||
const prob = parseInt(e.target.value)
|
||||
setInitialProb(prob)
|
||||
if (prob < 5 || prob > 95)
|
||||
setProbErrorText('Probability must be between 5% and 95%')
|
||||
else setProbErrorText('')
|
||||
}}
|
||||
/>
|
||||
<span className={'mt-2'}>%</span>
|
||||
</>
|
||||
)}
|
||||
<span className={'mt-2 ml-0.5'}>%</span>
|
||||
</div>
|
||||
</ChoicesToggleGroup>
|
||||
</Row>
|
||||
{probErrorText && (
|
||||
<div className="text-error mt-2 ml-1 text-sm">{probErrorText}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -289,7 +283,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
<div className="form-control mb-1 items-start">
|
||||
<label className="label mb-1 gap-2">
|
||||
<span className="mb-1">Description</span>
|
||||
<InfoTooltip text="Optional. Describe how you will resolve this market." />
|
||||
<InfoTooltip text="Optional. Describe how you will resolve this question." />
|
||||
</label>
|
||||
<Textarea
|
||||
className="textarea textarea-bordered w-full resize-none"
|
||||
|
@ -328,41 +322,47 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
|
||||
<div className="form-control mb-1 items-start">
|
||||
<label className="label mb-1 gap-2">
|
||||
<span>Question expires in a:</span>
|
||||
<InfoTooltip text="Betting will be halted after this date (local timezone)." />
|
||||
<span>Question closes in:</span>
|
||||
<InfoTooltip text="Betting will be halted after this time (local timezone)." />
|
||||
</label>
|
||||
<Row className={'w-full items-center gap-2'}>
|
||||
<ChoicesToggleGroup
|
||||
currentChoice={
|
||||
closeDate
|
||||
? [1, 7, 30, 365, 0].includes(
|
||||
dayjs(closeDate).diff(dayjs(), 'day')
|
||||
)
|
||||
? dayjs(closeDate).diff(dayjs(), 'day')
|
||||
: 0
|
||||
: -1
|
||||
}
|
||||
setChoice={setCloseDateInDays}
|
||||
choices={[1, 7, 30, 0]}
|
||||
titles={['Day', 'Week', 'Month', 'Custom']}
|
||||
currentChoice={dayjs(`${closeDate}T23:59`).diff(dayjs(), 'day')}
|
||||
setChoice={(choice) => {
|
||||
setCloseDateInDays(choice as number)
|
||||
}}
|
||||
choicesMap={{
|
||||
'A day': 1,
|
||||
'A week': 7,
|
||||
'30 days': 30,
|
||||
'This year': daysLeftInTheYear,
|
||||
}}
|
||||
isSubmitting={isSubmitting}
|
||||
className={'col-span-4 sm:col-span-2'}
|
||||
/>
|
||||
</Row>
|
||||
{showCalendar && (
|
||||
<Row>
|
||||
<input
|
||||
type={'date'}
|
||||
className="input input-bordered mt-4"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={(e) =>
|
||||
setCloseDate(
|
||||
dayjs(e.target.value).format('YYYY-MM-DDT23:59') || ''
|
||||
)
|
||||
setCloseDate(dayjs(e.target.value).format('YYYY-MM-DD') || '')
|
||||
}
|
||||
min={Date.now()}
|
||||
disabled={isSubmitting}
|
||||
value={dayjs(closeDate).format('YYYY-MM-DD')}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
type={'time'}
|
||||
className="input input-bordered mt-4 ml-2"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={(e) => setCloseHoursMinutes(e.target.value)}
|
||||
min={'00:00'}
|
||||
disabled={isSubmitting}
|
||||
value={closeHoursMinutes}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
<Spacer h={4} />
|
||||
|
@ -373,7 +373,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
{mustWaitForDailyFreeMarketStatus != 'loading' &&
|
||||
mustWaitForDailyFreeMarketStatus && (
|
||||
<InfoTooltip
|
||||
text={`Cost to create your market. This amount is used to subsidize trading.`}
|
||||
text={`Cost to create your question. This amount is used to subsidize betting.`}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
|
@ -432,7 +432,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
submit()
|
||||
}}
|
||||
>
|
||||
{isSubmitting ? 'Creating...' : 'Create market'}
|
||||
{isSubmitting ? 'Creating...' : 'Create question'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue
Block a user