diff --git a/web/components/choices-toggle-group.tsx b/web/components/choices-toggle-group.tsx index 4e23dfdc..0120aea9 100644 --- a/web/components/choices-toggle-group.tsx +++ b/web/components/choices-toggle-group.tsx @@ -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 ( - - {choices.map((choice, i) => { - return ( - null} + className="mt-2" + > + + {Object.keys(choicesMap).map((choiceKey) => ( + 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]} - - ) - })} - + {choiceKey} + + ))} + {children} + + ) } diff --git a/web/components/contract/contract-details.tsx b/web/components/contract/contract-details.tsx index 801609c1..0115c746 100644 --- a/web/components/contract/contract-details.tsx +++ b/web/components/contract/contract-details.tsx @@ -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') diff --git a/web/pages/create.tsx b/web/pages/create.tsx index 1e01d2da..f9bbbee5 100644 --- a/web/pages/create.tsx +++ b/web/pages/create.tsx @@ -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('') // const [tagText, setTagText] = useState(tag ?? '') @@ -88,21 +85,26 @@ export function NewContract(props: { question: string; tag?: string }) { // const [anteError, setAnteError] = useState() // 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(weekFromToday) - + const [closeHoursMinutes, setCloseHoursMinutes] = useState('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 ( - Answer type + Answer type - - - setOutcomeType('BINARY')} - disabled={isSubmitting} - /> - Yes / No - - - - setOutcomeType('FREE_RESPONSE')} - disabled={isSubmitting} - /> - Free response - - - setOutcomeType('NUMERIC')} - /> - Numeric (experimental) - - + { + 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 && ( + + {marketInfoText} + + )} {outcomeType === 'BINARY' && ( How likely is it to happen? - setShowNumInput(!showNumInput)} - /> - + { + setProbErrorText('') + setInitialProb(option as number) + }} + choicesMap={{ + Unlikely: 25, + 'Not Sure': 50, + Likely: 75, + }} isSubmitting={isSubmitting} - /> - {showNumInput && ( - <> + className={'col-span-4 sm:col-span-3'} + > + - 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('') + }} /> - % - > - )} + % + + + {probErrorText && ( + {probErrorText} + )} )} @@ -289,7 +283,7 @@ export function NewContract(props: { question: string; tag?: string }) { Description - + - Question expires in a: - + Question closes in: + { + 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'} /> - {showCalendar && ( + 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')} /> - )} + e.stopPropagation()} + onChange={(e) => setCloseHoursMinutes(e.target.value)} + min={'00:00'} + disabled={isSubmitting} + value={closeHoursMinutes} + /> + @@ -373,7 +373,7 @@ export function NewContract(props: { question: string; tag?: string }) { {mustWaitForDailyFreeMarketStatus != 'loading' && mustWaitForDailyFreeMarketStatus && ( )} @@ -432,7 +432,7 @@ export function NewContract(props: { question: string; tag?: string }) { submit() }} > - {isSubmitting ? 'Creating...' : 'Create market'} + {isSubmitting ? 'Creating...' : 'Create question'}