multipe choice answers
This commit is contained in:
parent
013ff1d941
commit
ea80c06e45
|
@ -4,13 +4,19 @@ import { JSONContent } from '@tiptap/core'
|
|||
import { GroupLink } from 'common/group'
|
||||
|
||||
export type AnyMechanism = DPM | CPMM
|
||||
export type AnyOutcomeType = Binary | PseudoNumeric | FreeResponse | Numeric
|
||||
export type AnyOutcomeType =
|
||||
| Binary
|
||||
| MultipleChoice
|
||||
| PseudoNumeric
|
||||
| FreeResponse
|
||||
| Numeric
|
||||
export type AnyContractType =
|
||||
| (CPMM & Binary)
|
||||
| (CPMM & PseudoNumeric)
|
||||
| (DPM & Binary)
|
||||
| (DPM & FreeResponse)
|
||||
| (DPM & Numeric)
|
||||
| (DPM & MultipleChoice)
|
||||
|
||||
export type Contract<T extends AnyContractType = AnyContractType> = {
|
||||
id: string
|
||||
|
@ -57,6 +63,7 @@ export type BinaryContract = Contract & Binary
|
|||
export type PseudoNumericContract = Contract & PseudoNumeric
|
||||
export type NumericContract = Contract & Numeric
|
||||
export type FreeResponseContract = Contract & FreeResponse
|
||||
export type MultipleChoiceContract = Contract & MultipleChoice
|
||||
export type DPMContract = Contract & DPM
|
||||
export type CPMMContract = Contract & CPMM
|
||||
export type DPMBinaryContract = BinaryContract & DPM
|
||||
|
@ -104,6 +111,13 @@ export type FreeResponse = {
|
|||
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
|
||||
}
|
||||
|
||||
export type MultipleChoice = {
|
||||
outcomeType: 'MULTIPLE_CHOICE'
|
||||
answers: Answer[]
|
||||
resolution?: string | 'MKT' | 'CANCEL'
|
||||
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
|
||||
}
|
||||
|
||||
export type Numeric = {
|
||||
outcomeType: 'NUMERIC'
|
||||
bucketCount: number
|
||||
|
@ -118,6 +132,7 @@ export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL'
|
|||
export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const
|
||||
export const OUTCOME_TYPES = [
|
||||
'BINARY',
|
||||
'MULTIPLE_CHOICE',
|
||||
'FREE_RESPONSE',
|
||||
'PSEUDO_NUMERIC',
|
||||
'NUMERIC',
|
||||
|
|
65
web/components/answers/multiple-choice-answers.tsx
Normal file
65
web/components/answers/multiple-choice-answers.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { MAX_ANSWER_LENGTH } from 'common/answer'
|
||||
import { useState } from 'react'
|
||||
import Textarea from 'react-expanding-textarea'
|
||||
import { XIcon } from '@heroicons/react/solid'
|
||||
|
||||
import { Col } from '../layout/col'
|
||||
import { Row } from '../layout/row'
|
||||
|
||||
export function MultipleChoiceAnswers(props: {
|
||||
setAnswers: (answers: string[]) => void
|
||||
}) {
|
||||
const [answers, setInternalAnswers] = useState(['', '', ''])
|
||||
|
||||
const setAnswer = (i: number, answer: string) => {
|
||||
const newAnswers = setElement(answers, i, answer)
|
||||
setInternalAnswers(newAnswers)
|
||||
props.setAnswers(newAnswers)
|
||||
}
|
||||
|
||||
const removeAnswer = (i: number) => {
|
||||
const newAnswers = answers.slice(0, i).concat(answers.slice(i + 1))
|
||||
setInternalAnswers(newAnswers)
|
||||
props.setAnswers(newAnswers)
|
||||
}
|
||||
|
||||
const addAnswer = () => setAnswer(answers.length, '')
|
||||
|
||||
return (
|
||||
<Col>
|
||||
{answers.map((answer, i) => (
|
||||
<Row className="mb-2 items-center align-middle">
|
||||
{i + 1}.{' '}
|
||||
<Textarea
|
||||
value={answer}
|
||||
onChange={(e) => setAnswer(i, e.target.value)}
|
||||
className="textarea textarea-bordered ml-2 w-full resize-none"
|
||||
placeholder="Type your answer..."
|
||||
rows={1}
|
||||
maxLength={MAX_ANSWER_LENGTH}
|
||||
/>
|
||||
{answers.length > 2 && (
|
||||
<button
|
||||
className="btn btn-xs btn-outline ml-2"
|
||||
onClick={() => removeAnswer(i)}
|
||||
>
|
||||
<XIcon className="h-4 w-4 flex-shrink-0" />
|
||||
</button>
|
||||
)}
|
||||
</Row>
|
||||
))}
|
||||
|
||||
<Row className="justify-end">
|
||||
<button className="btn btn-outline btn-xs" onClick={addAnswer}>
|
||||
Add answer
|
||||
</button>
|
||||
</Row>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
const setElement = <T,>(array: T[], i: number, elem: T) => {
|
||||
const newArray = array.concat()
|
||||
newArray[i] = elem
|
||||
return newArray
|
||||
}
|
|
@ -7,6 +7,7 @@ import {
|
|||
BinaryContract,
|
||||
Contract,
|
||||
FreeResponseContract,
|
||||
MultipleChoiceContract,
|
||||
resolution,
|
||||
} from 'common/contract'
|
||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||
|
@ -77,7 +78,7 @@ export function BinaryContractOutcomeLabel(props: {
|
|||
}
|
||||
|
||||
export function FreeResponseOutcomeLabel(props: {
|
||||
contract: FreeResponseContract
|
||||
contract: FreeResponseContract | MultipleChoiceContract
|
||||
resolution: string | 'CANCEL' | 'MKT'
|
||||
truncate: 'short' | 'long' | 'none'
|
||||
answerClassName?: string
|
||||
|
|
|
@ -31,6 +31,7 @@ import { Checkbox } from 'web/components/checkbox'
|
|||
import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
|
||||
import { Title } from 'web/components/title'
|
||||
import { SEO } from 'web/components/SEO'
|
||||
import { MultipleChoiceAnswers } from 'web/components/answers/multiple-choice-answers'
|
||||
|
||||
export const getServerSideProps = redirectIfLoggedOut('/')
|
||||
|
||||
|
@ -116,6 +117,8 @@ export function NewContract(props: {
|
|||
const [isLogScale, setIsLogScale] = useState<boolean>(!!params?.isLogScale)
|
||||
const [initialValueString, setInitialValueString] = useState(initValue)
|
||||
|
||||
const [answers, setAnswers] = useState<string[]>([]) // for multiple choice
|
||||
|
||||
useEffect(() => {
|
||||
if (groupId && creator)
|
||||
getGroup(groupId).then((group) => {
|
||||
|
@ -160,6 +163,10 @@ export function NewContract(props: {
|
|||
// get days from today until the end of this year:
|
||||
const daysLeftInTheYear = dayjs().endOf('year').diff(dayjs(), 'day')
|
||||
|
||||
const isValidMultipleChoice = answers.every(
|
||||
(answer) => answer.trim().length > 0
|
||||
)
|
||||
|
||||
const isValid =
|
||||
(outcomeType === 'BINARY' ? initialProb >= 5 && initialProb <= 95 : true) &&
|
||||
question.length > 0 &&
|
||||
|
@ -178,7 +185,8 @@ export function NewContract(props: {
|
|||
min < max &&
|
||||
max - min > 0.01 &&
|
||||
min < initialValue &&
|
||||
initialValue < max))
|
||||
initialValue < max)) &&
|
||||
(outcomeType !== 'MULTIPLE_CHOICE' || isValidMultipleChoice)
|
||||
|
||||
const [errorText, setErrorText] = useState<string>('')
|
||||
useEffect(() => {
|
||||
|
@ -221,6 +229,7 @@ export function NewContract(props: {
|
|||
max,
|
||||
initialValue,
|
||||
isLogScale,
|
||||
answers,
|
||||
groupId: selectedGroup?.id,
|
||||
})
|
||||
)
|
||||
|
@ -259,10 +268,11 @@ export function NewContract(props: {
|
|||
'Users can submit their own answers to this market.'
|
||||
)
|
||||
else setMarketInfoText('')
|
||||
setOutcomeType(choice as 'BINARY' | 'FREE_RESPONSE')
|
||||
setOutcomeType(choice as outcomeType)
|
||||
}}
|
||||
choicesMap={{
|
||||
'Yes / No': 'BINARY',
|
||||
'Multiple choice': 'MULTIPLE_CHOICE',
|
||||
'Free response': 'FREE_RESPONSE',
|
||||
Numeric: 'PSEUDO_NUMERIC',
|
||||
}}
|
||||
|
@ -277,6 +287,10 @@ export function NewContract(props: {
|
|||
|
||||
<Spacer h={6} />
|
||||
|
||||
{outcomeType === 'MULTIPLE_CHOICE' && (
|
||||
<MultipleChoiceAnswers setAnswers={setAnswers} />
|
||||
)}
|
||||
|
||||
{outcomeType === 'PSEUDO_NUMERIC' && (
|
||||
<>
|
||||
<div className="form-control mb-2 items-start">
|
||||
|
|
Loading…
Reference in New Issue
Block a user