Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9/18 Tweaks #103

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 53 additions & 24 deletions packages/markets/components/CreateMarketForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import _ from 'lodash'
import { ToggleLeftIcon, XIcon, CircleIcon, CircleDotIcon, PlusIcon } from 'lucide-react'
import moment from 'moment'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { CirclePicker } from 'react-color'
import { useFieldArray, useForm } from 'react-hook-form'
import { mutate } from 'swr'
Expand All @@ -27,6 +27,9 @@ import { Popover, PopoverContent, PopoverTrigger } from '@play-money/ui/popover'
import { RadioGroup, RadioGroupItem } from '@play-money/ui/radio-group'
import { toast } from '@play-money/ui/use-toast'
import { cn } from '@play-money/ui/utils'
import { clearPresistedData, getPersistedData, usePersistForm } from '../../ui/src/hooks/usePersistForm'

const CREATE_MARKET_FORM_KEY = 'create-market-form'

const COLORS = [
'#f44336',
Expand Down Expand Up @@ -68,25 +71,39 @@ export function CreateMarketForm({ onSuccess }: { onSuccess?: () => Promise<void
const router = useRouter()
const tzName = /\((?<tz>[A-Za-z\s].*)\)/.exec(new Date().toString())?.groups?.tz ?? null

const getDefaultValues = useMemo(
() =>
getPersistedData<MarketCreateFormValues>({
defaultValue: {
question: '',
type: 'binary',
description: '',
closeDate: moment().add(1, 'month').endOf('day').toDate(),
options: [
{ name: 'Yes', color: SHUFFLED_COLORS[0] },
{ name: 'No', color: SHUFFLED_COLORS[1] },
],
tags: [],
},
localStorageKey: CREATE_MARKET_FORM_KEY,
}),
[]
)

const form = useForm<MarketCreateFormValues>({
resolver: zodResolver(marketCreateFormSchema),
defaultValues: {
question: '',
type: 'binary',
description: '',
closeDate: moment().add(1, 'month').endOf('day').toDate(),
options: [
{ name: 'Yes', color: SHUFFLED_COLORS[0] },
{ name: 'No', color: SHUFFLED_COLORS[1] },
],
tags: [],
},
defaultValues: getDefaultValues,
})

usePersistForm({ value: form.getValues(), localStorageKey: CREATE_MARKET_FORM_KEY })

async function onSubmit(market: MarketCreateFormValues) {
try {
const newMarket = await createMarket(market)

clearPresistedData({ localStorageKey: CREATE_MARKET_FORM_KEY })
form.reset({})
form.reset({}) // Requires double reset to work: https://github.com/orgs/react-hook-form/discussions/7589#discussioncomment-8295031
onSuccess?.()
void mutate(MY_BALANCE_PATH)
toast({
Expand All @@ -103,7 +120,7 @@ export function CreateMarketForm({ onSuccess }: { onSuccess?: () => Promise<void

const handleSubmit = form.handleSubmit(onSubmit)

const { fields, replace, append, remove } = useFieldArray({
const { fields, replace, append, remove, update } = useFieldArray({
control: form.control,
name: 'options',
})
Expand All @@ -113,16 +130,28 @@ export function CreateMarketForm({ onSuccess }: { onSuccess?: () => Promise<void
useEffect(
function replaceOptionsIfMulti() {
if (type === 'binary') {
replace([
{ name: 'Yes', color: SHUFFLED_COLORS[0] },
{ name: 'No', color: SHUFFLED_COLORS[1] },
])
} else if (type === 'multi') {
replace([
{ name: '', color: SHUFFLED_COLORS[0] },
{ name: '', color: SHUFFLED_COLORS[1] },
{ name: '', color: SHUFFLED_COLORS[2] },
])
const options = form.getValues('options') || []

if (options[0] && !options[0].name) {
update(0, { ...options[0], name: 'Yes' })
}

if (options[1] && !options[1].name) {
update(1, { ...options[1], name: 'No' })
}

if (options.length > 2 && !options[2].name) {
remove(2)
}
} else if (type === 'multi' && fields.length === 2) {
const options = form.getValues('options')
if (options[0].name === 'Yes') {
update(0, { ...options[0], name: '' })
}
if (options[1].name === 'No') {
update(1, { ...options[1], name: '' })
}
append({ name: '', color: SHUFFLED_COLORS[2] })
}
},
[type]
Expand Down Expand Up @@ -227,7 +256,7 @@ export function CreateMarketForm({ onSuccess }: { onSuccess?: () => Promise<void
<Card className="divide-y">
{fields.map((fieldItem, index) => (
<div className="relative flex gap-1 p-2" key={fieldItem.id}>
{type === 'multi' && index > 2 ? (
{(type === 'binary' && index > 1) || (type === 'multi' && index > 2) ? (
<Button
variant="outline"
size="icon"
Expand Down
49 changes: 45 additions & 4 deletions packages/markets/components/MarketGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
import { format } from 'date-fns'
import _ from 'lodash'
import React from 'react'
import { LineChart, Line, ResponsiveContainer, YAxis, Tooltip as ChartTooltip } from 'recharts'
import { LineChart, Line, ResponsiveContainer, YAxis, XAxis, CartesianGrid, Tooltip as ChartTooltip } from 'recharts'
import { useMarketGraph } from '@play-money/api-helpers/client/hooks'
import { Card } from '@play-money/ui/card'
import { ExtendedMarket } from '../types'

function CustomizedXAxisTick({ x, y, payload }: { x: number; y: number; payload: { value: string } }) {
return (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={5} dx={-4} textAnchor="end" className="fill-muted-foreground/50">
{format(payload.value, 'MMM d')}
</text>
</g>
)
}

function CustomizedYAxisTick({ x, y, payload }: { x: number; y: number; payload: { value: string | number } }) {
return payload.value !== 0 ? (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={4} dx={2} textAnchor="start" className="fill-muted-foreground/50">
{payload.value}%
</text>
</g>
) : (
<g />
)
}

export function MarketGraph({ market, activeOptionId }: { market: ExtendedMarket; activeOptionId: string }) {
const { data: graph } = useMarketGraph({ marketId: market.id })
const activeOptionIndex = market.options.findIndex((o) => o.id === activeOptionId)

return (
<Card className="h-32 p-4">
<Card className="h-40">
{graph?.data ? (
<ResponsiveContainer width="100%" height="100%">
<LineChart width={300} height={128} data={graph.data}>
<LineChart width={300} height={128} data={graph.data} margin={{ top: 10, right: 0, bottom: 0, left: 0 }}>
<ChartTooltip
content={({ payload }) => {
const data = payload?.[0]?.payload
Expand All @@ -33,7 +55,26 @@ export function MarketGraph({ market, activeOptionId }: { market: ExtendedMarket
return null
}}
/>
<YAxis type="number" domain={[0, 100]} hide />
<XAxis
height={20}
dataKey="endAt"
stroke="hsl(var(--border))"
// axisLine={false}
className="font-mono text-[10px] uppercase"
minTickGap={80}
tick={CustomizedXAxisTick}
tickFormatter={(value) => format(value, 'MMM d')}
/>
<YAxis
type="number"
domain={[0, 100]}
width={40}
stroke="hsl(var(--border))"
className="font-mono text-[10px] uppercase"
orientation="right"
tick={CustomizedYAxisTick}
tickFormatter={(value, i) => (value !== 0 && value !== 100 ? `${value}%` : '')}
/>
{market.options.map((option, i) => (
<Line
key={option.id}
Expand Down
2 changes: 1 addition & 1 deletion packages/markets/components/MarketOverviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function MarketOverviewPage({

<CardHeader className="pt-0 md:pt-0">
<CardTitle className="leading-relaxed">{market.question}</CardTitle>
<div className="flex flex-row flex-wrap gap-x-4 gap-y-2 font-mono text-sm text-muted-foreground md:flex-nowrap">
<div className="flex flex-row flex-wrap gap-x-4 gap-y-2 text-sm text-muted-foreground md:flex-nowrap">
{!market.marketResolution ? (
<div style={{ color: mostLikelyOption.color }} className="flex-shrink-0 font-medium">
{Math.round(mostLikelyOption.probability || 0)}% {_.truncate(mostLikelyOption.name, { length: 30 })}
Expand Down
2 changes: 1 addition & 1 deletion packages/markets/components/MarketPositionsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function MarketPositionsPage({

<CardHeader className="pt-0 md:pt-0">
<CardTitle className="leading-relaxed">{market.question}</CardTitle>
<div className="flex flex-row flex-wrap gap-x-4 gap-y-2 font-mono text-sm text-muted-foreground md:flex-nowrap">
<div className="flex flex-row flex-wrap gap-x-4 gap-y-2 text-sm text-muted-foreground md:flex-nowrap">
{!market.marketResolution ? (
<div style={{ color: mostLikelyOption.color }} className="flex-shrink-0 font-medium">
{Math.round(mostLikelyOption.probability || 0)}% {_.truncate(mostLikelyOption.name, { length: 30 })}
Expand Down
41 changes: 41 additions & 0 deletions packages/ui/src/hooks/usePersistForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client'

import _ from 'lodash'
import { useEffect } from 'react'

export const usePersistForm = ({ value, localStorageKey }: { value: unknown; localStorageKey: string }) => {
useEffect(() => {
if (_.isEmpty(value)) {
localStorage.removeItem(localStorageKey)
} else {
localStorage.setItem(localStorageKey, JSON.stringify(value))
}
}, [value, localStorageKey])
}

export const getPersistedData = <T>({
defaultValue,
localStorageKey,
}: {
defaultValue: T
localStorageKey: string
}): T => {
const data = localStorage.getItem(localStorageKey)

if (data) {
try {
const savedData = JSON.parse(data) as T
if (_.isEmpty(savedData)) {
return defaultValue
}
return savedData
} catch (err) {
return defaultValue
}
}
return defaultValue
}

export function clearPresistedData({ localStorageKey }: { localStorageKey: string }) {
localStorage.removeItem(localStorageKey)
}
1 change: 1 addition & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './contexts/EditorExtensionsContext'
export * from './hooks/useLocalStorage'
export * from './hooks/usePersistForm'
export * from './hooks/useSearchParam'
export * from './helpers'
49 changes: 45 additions & 4 deletions packages/users/components/UserGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,35 @@

import { format } from 'date-fns'
import React from 'react'
import { AreaChart, ResponsiveContainer, YAxis, Tooltip as ChartTooltip, Area } from 'recharts'
import { AreaChart, ResponsiveContainer, YAxis, XAxis, Tooltip as ChartTooltip, Area } from 'recharts'
import { useUserGraph } from '@play-money/api-helpers/client/hooks'
import { CurrencyDisplay } from '@play-money/finance/components/CurrencyDisplay'
import { formatNumber } from '@play-money/finance/lib/formatCurrency'
import { Card } from '@play-money/ui/card'
import { cn } from '@play-money/ui/utils'

function CustomizedXAxisTick({ x, y, payload }: { x: number; y: number; payload: { value: string } }) {
return (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={5} dx={-4} textAnchor="end" className="fill-muted-foreground/50">
{format(payload.value, 'MMM d')}
</text>
</g>
)
}

function CustomizedYAxisTick({ x, y, payload }: { x: number; y: number; payload: { value: number } }) {
return payload.value !== 0 ? (
<g transform={`translate(${x},${y})`}>
<text x={0} y={0} dy={4} dx={2} textAnchor="start" className="fill-muted-foreground/50">
¤{formatNumber(payload.value)}
</text>
</g>
) : (
<g />
)
}

const BALANCE_COLOR = '#333'
const LIQUIDITY_COLOR = '#7c3aed'
const MARKET_COLOR = '#facc15'
Expand Down Expand Up @@ -35,10 +58,10 @@ export function UserGraph({ userId }: { userId: string }) {
</div>
) : null}
</div>
<div className="h-32 p-4">
<div className="h-40">
{graph?.data ? (
<ResponsiveContainer width="100%" height="100%">
<AreaChart width={300} height={128} data={graph.data}>
<AreaChart width={300} height={128} data={graph.data} margin={{ top: 10, right: 0, bottom: 0, left: 0 }}>
<ChartTooltip
content={({ payload }) => {
const data = payload?.[0]?.payload
Expand All @@ -61,7 +84,25 @@ export function UserGraph({ userId }: { userId: string }) {
return null
}}
/>
<YAxis type="number" domain={[0, 1]} hide />
<XAxis
height={20}
dataKey="endAt"
stroke="hsl(var(--border))"
// axisLine={false}
className="font-mono text-[10px] uppercase"
minTickGap={80}
tick={CustomizedXAxisTick}
tickFormatter={(value) => format(value, 'MMM d')}
/>
<YAxis
type="number"
width={50}
stroke="hsl(var(--border))"
className="font-mono text-[10px] uppercase"
orientation="right"
tick={CustomizedYAxisTick}
tickFormatter={(value, i) => (value !== 0 && value !== 100 ? `${value}%` : '')}
/>

<defs>
<linearGradient id="fillLiquidity" x1="0" y1="0" x2="0" y2="1">
Expand Down