parent
d6bb27f97c
commit
c115b5cca7
|
@ -20,11 +20,11 @@ import { AnswerBetPanel } from 'web/components/answers/answer-bet-panel'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
import { Avatar } from 'web/components/avatar'
|
import { Avatar } from 'web/components/avatar'
|
||||||
import { Linkify } from 'web/components/linkify'
|
import { Linkify } from 'web/components/linkify'
|
||||||
import { BuyButton } from 'web/components/yes-no-selector'
|
|
||||||
import { UserLink } from 'web/components/user-link'
|
|
||||||
import { Button } from 'web/components/button'
|
import { Button } from 'web/components/button'
|
||||||
import { useAdmin } from 'web/hooks/use-admin'
|
import { useAdmin } from 'web/hooks/use-admin'
|
||||||
import { needsAdminToResolve } from 'web/pages/[username]/[contractSlug]'
|
import { needsAdminToResolve } from 'web/pages/[username]/[contractSlug]'
|
||||||
|
import { CATEGORY_COLORS } from '../charts/contract/choice'
|
||||||
|
import { useChartAnswers } from '../charts/contract/choice'
|
||||||
|
|
||||||
export function AnswersPanel(props: {
|
export function AnswersPanel(props: {
|
||||||
contract: FreeResponseContract | MultipleChoiceContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
|
@ -38,6 +38,7 @@ export function AnswersPanel(props: {
|
||||||
const answers = (useAnswers(contract.id) ?? contract.answers).filter(
|
const answers = (useAnswers(contract.id) ?? contract.answers).filter(
|
||||||
(a) => a.number != 0 || contract.outcomeType === 'MULTIPLE_CHOICE'
|
(a) => a.number != 0 || contract.outcomeType === 'MULTIPLE_CHOICE'
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasZeroBetAnswers = answers.some((answer) => totalBets[answer.id] < 1)
|
const hasZeroBetAnswers = answers.some((answer) => totalBets[answer.id] < 1)
|
||||||
|
|
||||||
const [winningAnswers, losingAnswers] = partition(
|
const [winningAnswers, losingAnswers] = partition(
|
||||||
|
@ -104,6 +105,10 @@ export function AnswersPanel(props: {
|
||||||
? 'checkbox'
|
? 'checkbox'
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
const colorSortedAnswer = useChartAnswers(contract).map(
|
||||||
|
(value, _index) => value.text
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className="gap-3">
|
<Col className="gap-3">
|
||||||
{(resolveOption || resolution) &&
|
{(resolveOption || resolution) &&
|
||||||
|
@ -128,7 +133,12 @@ export function AnswersPanel(props: {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{answerItems.map((item) => (
|
{answerItems.map((item) => (
|
||||||
<OpenAnswer key={item.id} answer={item} contract={contract} />
|
<OpenAnswer
|
||||||
|
key={item.id}
|
||||||
|
answer={item}
|
||||||
|
contract={contract}
|
||||||
|
colorIndex={colorSortedAnswer.indexOf(item.text)}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
{hasZeroBetAnswers && !showAllAnswers && (
|
{hasZeroBetAnswers && !showAllAnswers && (
|
||||||
<Button
|
<Button
|
||||||
|
@ -174,15 +184,18 @@ export function AnswersPanel(props: {
|
||||||
function OpenAnswer(props: {
|
function OpenAnswer(props: {
|
||||||
contract: FreeResponseContract | MultipleChoiceContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
answer: Answer
|
answer: Answer
|
||||||
|
colorIndex: number | undefined
|
||||||
}) {
|
}) {
|
||||||
const { answer, contract } = props
|
const { answer, contract, colorIndex } = props
|
||||||
const { username, avatarUrl, name, text } = answer
|
const { username, avatarUrl, text } = answer
|
||||||
const prob = getDpmOutcomeProbability(contract.totalShares, answer.id)
|
const prob = getDpmOutcomeProbability(contract.totalShares, answer.id)
|
||||||
const probPercent = formatPercent(prob)
|
const probPercent = formatPercent(prob)
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const color =
|
||||||
|
colorIndex != undefined ? CATEGORY_COLORS[colorIndex] : '#B1B1C7'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className="border-base-200 bg-base-200 relative flex-1 rounded-md px-2">
|
<Col className="my-1 px-2">
|
||||||
<Modal open={open} setOpen={setOpen} position="center">
|
<Modal open={open} setOpen={setOpen} position="center">
|
||||||
<AnswerBetPanel
|
<AnswerBetPanel
|
||||||
answer={answer}
|
answer={answer}
|
||||||
|
@ -193,40 +206,44 @@ function OpenAnswer(props: {
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<div
|
<Col
|
||||||
className="pointer-events-none absolute -mx-2 h-full rounded-tl-md bg-green-600 bg-opacity-10"
|
className={clsx(
|
||||||
style={{ width: `${100 * Math.max(prob, 0.01)}%` }}
|
'bg-greyscale-1 relative w-full rounded-lg transition-all',
|
||||||
/>
|
tradingAllowed(contract) ? 'text-greyscale-7' : 'text-greyscale-5'
|
||||||
|
)}
|
||||||
<Row className="my-4 gap-3">
|
>
|
||||||
<Avatar className="mx-1" username={username} avatarUrl={avatarUrl} />
|
<Row className="z-20 -mb-1 justify-between gap-2 py-2 px-3">
|
||||||
<Col className="min-w-0 flex-1 lg:gap-1">
|
<Row>
|
||||||
<div className="text-sm text-gray-500">
|
<Avatar
|
||||||
<UserLink username={username} name={name} /> answered
|
className="mt-0.5 mr-2 inline h-5 w-5 border border-transparent transition-transform hover:border-none"
|
||||||
</div>
|
username={username}
|
||||||
|
avatarUrl={avatarUrl}
|
||||||
<Col className="align-items justify-between gap-4 sm:flex-row">
|
/>
|
||||||
<Linkify className="whitespace-pre-line text-lg" text={text} />
|
<Linkify
|
||||||
<Row className="align-items items-center justify-end gap-4">
|
className="text-md cursor-pointer whitespace-pre-line"
|
||||||
<span
|
text={text}
|
||||||
className={clsx(
|
/>
|
||||||
'text-2xl',
|
</Row>
|
||||||
tradingAllowed(contract) ? 'text-primary' : 'text-gray-500'
|
<Row className="gap-2">
|
||||||
)}
|
<div className="my-auto text-xl">{probPercent}</div>
|
||||||
>
|
{tradingAllowed(contract) && (
|
||||||
{probPercent}
|
<Button
|
||||||
</span>
|
size="2xs"
|
||||||
<BuyButton
|
color="gray-outline"
|
||||||
className={clsx(
|
|
||||||
'btn-sm flex-initial !px-6 sm:flex',
|
|
||||||
tradingAllowed(contract) ? '' : '!hidden'
|
|
||||||
)}
|
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
/>
|
className="my-auto"
|
||||||
</Row>
|
>
|
||||||
</Col>
|
BUY
|
||||||
</Col>
|
</Button>
|
||||||
</Row>
|
)}
|
||||||
|
</Row>
|
||||||
|
</Row>
|
||||||
|
<hr
|
||||||
|
color={color}
|
||||||
|
className="absolute z-0 h-full w-full rounded-l-lg border-none opacity-30"
|
||||||
|
style={{ width: `${100 * Math.max(prob, 0.01)}%` }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ export type ColorType =
|
||||||
| 'indigo'
|
| 'indigo'
|
||||||
| 'yellow'
|
| 'yellow'
|
||||||
| 'gray'
|
| 'gray'
|
||||||
|
| 'gray-outline'
|
||||||
| 'gradient'
|
| 'gradient'
|
||||||
| 'gray-white'
|
| 'gray-white'
|
||||||
| 'highlight-blue'
|
| 'highlight-blue'
|
||||||
|
@ -63,6 +64,8 @@ export function Button(props: {
|
||||||
'disabled:bg-greyscale-2 bg-indigo-500 text-white hover:bg-indigo-600',
|
'disabled:bg-greyscale-2 bg-indigo-500 text-white hover:bg-indigo-600',
|
||||||
color === 'gray' &&
|
color === 'gray' &&
|
||||||
'bg-greyscale-1 text-greyscale-6 hover:bg-greyscale-2 disabled:opacity-50',
|
'bg-greyscale-1 text-greyscale-6 hover:bg-greyscale-2 disabled:opacity-50',
|
||||||
|
color === 'gray-outline' &&
|
||||||
|
'border-greyscale-4 text-greyscale-4 hover:bg-greyscale-4 border-2 hover:text-white disabled:opacity-50',
|
||||||
color === 'gradient' &&
|
color === 'gradient' &&
|
||||||
'disabled:bg-greyscale-2 border-none bg-gradient-to-r from-indigo-500 to-blue-500 text-white hover:from-indigo-700 hover:to-blue-700',
|
'disabled:bg-greyscale-2 border-none bg-gradient-to-r from-indigo-500 to-blue-500 text-white hover:from-indigo-700 hover:to-blue-700',
|
||||||
color === 'gray-white' &&
|
color === 'gray-white' &&
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { Row } from 'web/components/layout/row'
|
||||||
import { Avatar } from 'web/components/avatar'
|
import { Avatar } from 'web/components/avatar'
|
||||||
|
|
||||||
// thanks to https://observablehq.com/@jonhelfman/optimal-orders-for-choosing-categorical-colors
|
// thanks to https://observablehq.com/@jonhelfman/optimal-orders-for-choosing-categorical-colors
|
||||||
const CATEGORY_COLORS = [
|
export const CATEGORY_COLORS = [
|
||||||
'#00b8dd',
|
'#00b8dd',
|
||||||
'#eecafe',
|
'#eecafe',
|
||||||
'#874c62',
|
'#874c62',
|
||||||
|
@ -144,6 +144,15 @@ const Legend = (props: { className?: string; items: LegendItem[] }) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useChartAnswers(
|
||||||
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
|
) {
|
||||||
|
return useMemo(
|
||||||
|
() => getTrackedAnswers(contract, CATEGORY_COLORS.length),
|
||||||
|
[contract]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const ChoiceContractChart = (props: {
|
export const ChoiceContractChart = (props: {
|
||||||
contract: FreeResponseContract | MultipleChoiceContract
|
contract: FreeResponseContract | MultipleChoiceContract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
|
@ -153,10 +162,7 @@ export const ChoiceContractChart = (props: {
|
||||||
}) => {
|
}) => {
|
||||||
const { contract, bets, width, height, onMouseOver } = props
|
const { contract, bets, width, height, onMouseOver } = props
|
||||||
const [start, end] = getDateRange(contract)
|
const [start, end] = getDateRange(contract)
|
||||||
const answers = useMemo(
|
const answers = useChartAnswers(contract)
|
||||||
() => getTrackedAnswers(contract, CATEGORY_COLORS.length),
|
|
||||||
[contract]
|
|
||||||
)
|
|
||||||
const betPoints = useMemo(() => getBetPoints(answers, bets), [answers, bets])
|
const betPoints = useMemo(() => getBetPoints(answers, bets), [answers, bets])
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
|
|
@ -244,7 +244,7 @@ function Button(props: {
|
||||||
type="button"
|
type="button"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'inline-flex flex-1 items-center justify-center rounded-md border border-transparent px-8 py-3 font-medium shadow-sm',
|
'inline-flex flex-1 items-center justify-center rounded-md border border-transparent px-8 py-3 font-medium shadow-sm',
|
||||||
color === 'green' && 'bg-teal-500 bg-teal-600 text-white',
|
color === 'green' && 'bg-teal-500 text-white hover:bg-teal-600',
|
||||||
color === 'red' && 'bg-red-400 text-white hover:bg-red-500',
|
color === 'red' && 'bg-red-400 text-white hover:bg-red-500',
|
||||||
color === 'yellow' && 'bg-yellow-400 text-white hover:bg-yellow-500',
|
color === 'yellow' && 'bg-yellow-400 text-white hover:bg-yellow-500',
|
||||||
color === 'blue' && 'bg-blue-400 text-white hover:bg-blue-500',
|
color === 'blue' && 'bg-blue-400 text-white hover:bg-blue-500',
|
||||||
|
|
|
@ -18,6 +18,7 @@ module.exports = {
|
||||||
colors: {
|
colors: {
|
||||||
'red-25': '#FDF7F6',
|
'red-25': '#FDF7F6',
|
||||||
'greyscale-1': '#FBFBFF',
|
'greyscale-1': '#FBFBFF',
|
||||||
|
'greyscale-1.5': '#F4F4FB',
|
||||||
'greyscale-2': '#E7E7F4',
|
'greyscale-2': '#E7E7F4',
|
||||||
'greyscale-3': '#D8D8EB',
|
'greyscale-3': '#D8D8EB',
|
||||||
'greyscale-4': '#B1B1C7',
|
'greyscale-4': '#B1B1C7',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user