Skip to content

Commit

Permalink
Merge pull request #103 from casesandberg/fix/9-18-tweaks
Browse files Browse the repository at this point in the history
9/18 Tweaks
  • Loading branch information
casesandberg authored Sep 18, 2024
2 parents df8ceb1 + fabd890 commit 24aab29
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 34 deletions.
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

0 comments on commit 24aab29

Please sign in to comment.