From e94d204a875a509a561c70d3c669420a8b699ef8 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Mon, 23 May 2022 06:35:50 -0600 Subject: [PATCH] Simplify market creation page (#268) * Simplify market creation page * Minor improvements * Prettier --- web/components/choices-toggle-group.tsx | 36 +++++++ web/pages/create.tsx | 131 ++++++++++++++++++------ 2 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 web/components/choices-toggle-group.tsx diff --git a/web/components/choices-toggle-group.tsx b/web/components/choices-toggle-group.tsx new file mode 100644 index 00000000..4e23dfdc --- /dev/null +++ b/web/components/choices-toggle-group.tsx @@ -0,0 +1,36 @@ +import { Row } from './layout/row' +import clsx from 'clsx' + +export function ChoicesToggleGroup(props: { + currentChoice: number + setChoice: (p: number) => void + choices: number[] + titles: string[] + isSubmitting?: boolean +}) { + 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' + return ( + +
+ {choices.map((choice, i) => { + return ( + + ) + })} +
+
+ ) +} diff --git a/web/pages/create.tsx b/web/pages/create.tsx index caf4927f..9dd8b1da 100644 --- a/web/pages/create.tsx +++ b/web/pages/create.tsx @@ -3,7 +3,6 @@ import { useEffect, useState } from 'react' import clsx from 'clsx' import dayjs from 'dayjs' import Textarea from 'react-expanding-textarea' - import { Spacer } from 'web/components/layout/spacer' import { useUser } from 'web/hooks/use-user' import { Contract, contractPath } from 'web/lib/firebase/contracts' @@ -11,14 +10,14 @@ import { createContract } from 'web/lib/firebase/api-call' import { FIXED_ANTE, MINIMUM_ANTE } from 'common/antes' import { InfoTooltip } from 'web/components/info-tooltip' import { Page } from 'web/components/page' -import { Title } from 'web/components/title' -import { ProbabilitySelector } from 'web/components/probability-selector' import { Row } from 'web/components/layout/row' import { MAX_DESCRIPTION_LENGTH, outcomeType } from 'common/contract' import { formatMoney } from 'common/util/format' import { useHasCreatedContractToday } from 'web/hooks/use-has-created-contract-today' -import { removeUndefinedProps } from '../../common/util/object' +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('') @@ -26,13 +25,13 @@ export default function Create() { return (
- - - <div className="rounded-lg bg-gray-100 px-6 py-4 shadow-md"> + <div className="rounded-lg px-6 py-4"> <form> <div className="form-control w-full"> <label className="label"> - <span className="mb-1">Question</span> + <span className="mb-1"> + Question<span className={'text-red-700'}>*</span> + </span> </label> <Textarea @@ -69,6 +68,8 @@ 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 ?? '') @@ -118,6 +119,12 @@ export function NewContract(props: { question: string; tag?: string }) { min < max && max - min > 0.01)) + function setCloseDateInDays(days: number) { + setShowCalendar(days === 0) + const newCloseDate = dayjs().add(days, 'day').format('YYYY-MM-DDT23:59') + setCloseDate(newCloseDate) + } + async function submit() { // TODO: Tell users why their contract is invalid if (!creator || !isValid) return @@ -146,15 +153,15 @@ export function NewContract(props: { question: string; tag?: string }) { const descriptionPlaceholder = outcomeType === 'BINARY' - ? `e.g. This market resolves to "YES" if, two weeks after closing, the...` + ? `e.g. This question resolves to "YES" if they receive the majority of votes...` : `e.g. I will choose the answer according to...` if (!creator) return <></> return ( <div> - <label className="label"> - <span className="mb-1">Answer type</span> + <label className="label mt-1"> + <span className="my-1">Answer type</span> </label> <Row className="form-control gap-2"> <label className="label cursor-pointer gap-2"> @@ -165,6 +172,7 @@ export function NewContract(props: { question: string; tag?: string }) { checked={outcomeType === 'BINARY'} value="BINARY" onChange={() => setOutcomeType('BINARY')} + disabled={isSubmitting} /> <span className="label-text">Yes / No</span> </label> @@ -177,6 +185,7 @@ export function NewContract(props: { question: string; tag?: string }) { checked={outcomeType === 'FREE_RESPONSE'} value="FREE_RESPONSE" onChange={() => setOutcomeType('FREE_RESPONSE')} + disabled={isSubmitting} /> <span className="label-text">Free response</span> </label> @@ -196,14 +205,46 @@ export function NewContract(props: { question: string; tag?: string }) { {outcomeType === 'BINARY' && ( <div className="form-control"> - <label className="label"> - <span className="mb-1">Initial probability</span> - </label> - - <ProbabilitySelector - probabilityInt={initialProb} - setProbabilityInt={setInitialProb} - /> + <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'}> + <ChoicesToggleGroup + currentChoice={initialProb} + setChoice={setInitialProb} + choices={[25, 50, 75]} + titles={['Unlikely', 'Unsure', 'Likely']} + isSubmitting={isSubmitting} + /> + {showNumInput && ( + <> + <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' + } + disabled={isSubmitting} + min={10} + max={90} + onChange={(e) => + setInitialProb(parseInt(e.target.value.substring(0, 2))) + } + /> + <span className={'mt-2'}>%</span> + </> + )} + </Row> </div> )} @@ -285,18 +326,41 @@ export function NewContract(props: { question: string; tag?: string }) { <div className="form-control mb-1 items-start"> <label className="label mb-1 gap-2"> - <span>Market close</span> - <InfoTooltip text="Trading will be halted after this time (local timezone)." /> + <span>Question expires in a:</span> + <InfoTooltip text="Betting will be halted after this date (local timezone)." /> </label> - <input - type="datetime-local" - className="input input-bordered" - onClick={(e) => e.stopPropagation()} - onChange={(e) => setCloseDate(e.target.value || '')} - min={Date.now()} - disabled={isSubmitting} - value={closeDate} - /> + <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']} + isSubmitting={isSubmitting} + /> + </Row> + {showCalendar && ( + <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') || '' + ) + } + min={Date.now()} + disabled={isSubmitting} + value={dayjs(closeDate).format('YYYY-MM-DD')} + /> + )} </div> <Spacer h={4} /> @@ -311,7 +375,12 @@ export function NewContract(props: { question: string; tag?: string }) { )} </label> {deservesDailyFreeMarket ? ( - <div className="label-text text-primary pl-1">FREE</div> + <div className="label-text text-primary pl-1"> + <span className={'label-text text-neutral line-through '}> + {formatMoney(ante)} + </span>{' '} + FREE + </div> ) : ( <div className="label-text text-neutral pl-1"> {formatMoney(ante)}