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'
|
import { GroupLink } from 'common/group'
|
||||||
|
|
||||||
export type AnyMechanism = DPM | CPMM
|
export type AnyMechanism = DPM | CPMM
|
||||||
export type AnyOutcomeType = Binary | PseudoNumeric | FreeResponse | Numeric
|
export type AnyOutcomeType =
|
||||||
|
| Binary
|
||||||
|
| MultipleChoice
|
||||||
|
| PseudoNumeric
|
||||||
|
| FreeResponse
|
||||||
|
| Numeric
|
||||||
export type AnyContractType =
|
export type AnyContractType =
|
||||||
| (CPMM & Binary)
|
| (CPMM & Binary)
|
||||||
| (CPMM & PseudoNumeric)
|
| (CPMM & PseudoNumeric)
|
||||||
| (DPM & Binary)
|
| (DPM & Binary)
|
||||||
| (DPM & FreeResponse)
|
| (DPM & FreeResponse)
|
||||||
| (DPM & Numeric)
|
| (DPM & Numeric)
|
||||||
|
| (DPM & MultipleChoice)
|
||||||
|
|
||||||
export type Contract<T extends AnyContractType = AnyContractType> = {
|
export type Contract<T extends AnyContractType = AnyContractType> = {
|
||||||
id: string
|
id: string
|
||||||
|
@ -57,6 +63,7 @@ export type BinaryContract = Contract & Binary
|
||||||
export type PseudoNumericContract = Contract & PseudoNumeric
|
export type PseudoNumericContract = Contract & PseudoNumeric
|
||||||
export type NumericContract = Contract & Numeric
|
export type NumericContract = Contract & Numeric
|
||||||
export type FreeResponseContract = Contract & FreeResponse
|
export type FreeResponseContract = Contract & FreeResponse
|
||||||
|
export type MultipleChoiceContract = Contract & MultipleChoice
|
||||||
export type DPMContract = Contract & DPM
|
export type DPMContract = Contract & DPM
|
||||||
export type CPMMContract = Contract & CPMM
|
export type CPMMContract = Contract & CPMM
|
||||||
export type DPMBinaryContract = BinaryContract & DPM
|
export type DPMBinaryContract = BinaryContract & DPM
|
||||||
|
@ -104,6 +111,13 @@ export type FreeResponse = {
|
||||||
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
|
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 = {
|
export type Numeric = {
|
||||||
outcomeType: 'NUMERIC'
|
outcomeType: 'NUMERIC'
|
||||||
bucketCount: number
|
bucketCount: number
|
||||||
|
@ -118,6 +132,7 @@ export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL'
|
||||||
export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const
|
export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const
|
||||||
export const OUTCOME_TYPES = [
|
export const OUTCOME_TYPES = [
|
||||||
'BINARY',
|
'BINARY',
|
||||||
|
'MULTIPLE_CHOICE',
|
||||||
'FREE_RESPONSE',
|
'FREE_RESPONSE',
|
||||||
'PSEUDO_NUMERIC',
|
'PSEUDO_NUMERIC',
|
||||||
'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,
|
BinaryContract,
|
||||||
Contract,
|
Contract,
|
||||||
FreeResponseContract,
|
FreeResponseContract,
|
||||||
|
MultipleChoiceContract,
|
||||||
resolution,
|
resolution,
|
||||||
} from 'common/contract'
|
} from 'common/contract'
|
||||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||||
|
@ -77,7 +78,7 @@ export function BinaryContractOutcomeLabel(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FreeResponseOutcomeLabel(props: {
|
export function FreeResponseOutcomeLabel(props: {
|
||||||
contract: FreeResponseContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
resolution: string | 'CANCEL' | 'MKT'
|
resolution: string | 'CANCEL' | 'MKT'
|
||||||
truncate: 'short' | 'long' | 'none'
|
truncate: 'short' | 'long' | 'none'
|
||||||
answerClassName?: string
|
answerClassName?: string
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { Checkbox } from 'web/components/checkbox'
|
||||||
import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
|
import { redirectIfLoggedOut } from 'web/lib/firebase/server-auth'
|
||||||
import { Title } from 'web/components/title'
|
import { Title } from 'web/components/title'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
|
import { MultipleChoiceAnswers } from 'web/components/answers/multiple-choice-answers'
|
||||||
|
|
||||||
export const getServerSideProps = redirectIfLoggedOut('/')
|
export const getServerSideProps = redirectIfLoggedOut('/')
|
||||||
|
|
||||||
|
@ -116,6 +117,8 @@ export function NewContract(props: {
|
||||||
const [isLogScale, setIsLogScale] = useState<boolean>(!!params?.isLogScale)
|
const [isLogScale, setIsLogScale] = useState<boolean>(!!params?.isLogScale)
|
||||||
const [initialValueString, setInitialValueString] = useState(initValue)
|
const [initialValueString, setInitialValueString] = useState(initValue)
|
||||||
|
|
||||||
|
const [answers, setAnswers] = useState<string[]>([]) // for multiple choice
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (groupId && creator)
|
if (groupId && creator)
|
||||||
getGroup(groupId).then((group) => {
|
getGroup(groupId).then((group) => {
|
||||||
|
@ -160,6 +163,10 @@ export function NewContract(props: {
|
||||||
// get days from today until the end of this year:
|
// get days from today until the end of this year:
|
||||||
const daysLeftInTheYear = dayjs().endOf('year').diff(dayjs(), 'day')
|
const daysLeftInTheYear = dayjs().endOf('year').diff(dayjs(), 'day')
|
||||||
|
|
||||||
|
const isValidMultipleChoice = answers.every(
|
||||||
|
(answer) => answer.trim().length > 0
|
||||||
|
)
|
||||||
|
|
||||||
const isValid =
|
const isValid =
|
||||||
(outcomeType === 'BINARY' ? initialProb >= 5 && initialProb <= 95 : true) &&
|
(outcomeType === 'BINARY' ? initialProb >= 5 && initialProb <= 95 : true) &&
|
||||||
question.length > 0 &&
|
question.length > 0 &&
|
||||||
|
@ -178,7 +185,8 @@ export function NewContract(props: {
|
||||||
min < max &&
|
min < max &&
|
||||||
max - min > 0.01 &&
|
max - min > 0.01 &&
|
||||||
min < initialValue &&
|
min < initialValue &&
|
||||||
initialValue < max))
|
initialValue < max)) &&
|
||||||
|
(outcomeType !== 'MULTIPLE_CHOICE' || isValidMultipleChoice)
|
||||||
|
|
||||||
const [errorText, setErrorText] = useState<string>('')
|
const [errorText, setErrorText] = useState<string>('')
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -221,6 +229,7 @@ export function NewContract(props: {
|
||||||
max,
|
max,
|
||||||
initialValue,
|
initialValue,
|
||||||
isLogScale,
|
isLogScale,
|
||||||
|
answers,
|
||||||
groupId: selectedGroup?.id,
|
groupId: selectedGroup?.id,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -259,10 +268,11 @@ export function NewContract(props: {
|
||||||
'Users can submit their own answers to this market.'
|
'Users can submit their own answers to this market.'
|
||||||
)
|
)
|
||||||
else setMarketInfoText('')
|
else setMarketInfoText('')
|
||||||
setOutcomeType(choice as 'BINARY' | 'FREE_RESPONSE')
|
setOutcomeType(choice as outcomeType)
|
||||||
}}
|
}}
|
||||||
choicesMap={{
|
choicesMap={{
|
||||||
'Yes / No': 'BINARY',
|
'Yes / No': 'BINARY',
|
||||||
|
'Multiple choice': 'MULTIPLE_CHOICE',
|
||||||
'Free response': 'FREE_RESPONSE',
|
'Free response': 'FREE_RESPONSE',
|
||||||
Numeric: 'PSEUDO_NUMERIC',
|
Numeric: 'PSEUDO_NUMERIC',
|
||||||
}}
|
}}
|
||||||
|
@ -277,6 +287,10 @@ export function NewContract(props: {
|
||||||
|
|
||||||
<Spacer h={6} />
|
<Spacer h={6} />
|
||||||
|
|
||||||
|
{outcomeType === 'MULTIPLE_CHOICE' && (
|
||||||
|
<MultipleChoiceAnswers setAnswers={setAnswers} />
|
||||||
|
)}
|
||||||
|
|
||||||
{outcomeType === 'PSEUDO_NUMERIC' && (
|
{outcomeType === 'PSEUDO_NUMERIC' && (
|
||||||
<>
|
<>
|
||||||
<div className="form-control mb-2 items-start">
|
<div className="form-control mb-2 items-start">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user