Skip to content

Commit

Permalink
Ingawei/undo delete wrapped (#3194)
Browse files Browse the repository at this point in the history
  • Loading branch information
ingawei authored Dec 6, 2024
1 parent 9705538 commit 6cdb616
Show file tree
Hide file tree
Showing 18 changed files with 1,307 additions and 2 deletions.
52 changes: 52 additions & 0 deletions backend/api/src/get-max-min-profit-2024.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { APIHandler } from 'api/helpers/endpoint'
import { createSupabaseDirectClient } from 'shared/supabase/init'

export const getmaxminprofit2024: APIHandler<
'get-max-min-profit-2024'
> = async (props) => {
const { userId } = props
const pg = createSupabaseDirectClient()

const data = await pg.manyOrNone(
`
with filtered_data as (
select
ucm.profit,
ucm.has_yes_shares,
ucm.has_no_shares,
ucm.answer_id,
c.data
from
user_contract_metrics ucm
join
contracts c on ucm.contract_id = c.id
where
ucm.user_id = $1
and c.token = 'MANA'
and c.resolution_time >= '2024-01-01'::timestamp
and c.resolution_time <= '2024-12-31 23:59:59'::timestamp
),
min_max_profits as (
select
max(profit) as max_profit,
min(profit) as min_profit
from
filtered_data
)
select
fd.profit,
fd.has_yes_shares,
fd.has_no_shares,
fd.answer_id,
fd.data
from
filtered_data fd
join
min_max_profits mmp on fd.profit = mmp.max_profit or fd.profit = mmp.min_profit
order by fd.profit desc;
`,
[userId]
)

return data
}
46 changes: 46 additions & 0 deletions backend/api/src/get-monthly-bets-2024.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { APIHandler } from 'api/helpers/endpoint'
import { createSupabaseDirectClient } from 'shared/supabase/init'

export const getmonthlybets2024: APIHandler<'get-monthly-bets-2024'> = async (
props
) => {
const { userId } = props
const pg = createSupabaseDirectClient()

const data = await pg.manyOrNone(
`
with months as (
select
to_char(generate_series(
'2024-01-01'::timestamp with time zone,
'2024-12-01'::timestamp with time zone,
'1 month'::interval
), 'YYYY-MM') as month
),
user_bets as (
select
to_char(date_trunc('month', created_time), 'YYYY-MM') as month,
count(*) as bet_count,
coalesce(sum(abs(amount)), 0) as total_amount
from contract_bets
where
user_id = $1
and created_time >= '2024-01-01'::timestamp with time zone
and created_time < '2025-01-01'::timestamp with time zone
and not coalesce(is_cancelled, false)
and not coalesce(is_redemption, false)
group by date_trunc('month', created_time)
)
select
months.month,
coalesce(user_bets.bet_count, 0) as bet_count,
coalesce(user_bets.total_amount, 0) as total_amount
from months
left join user_bets on months.month = user_bets.month
order by months.month
`,
[userId]
)

return data
}
4 changes: 4 additions & 0 deletions backend/api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ import { generateAIMarketSuggestions } from './generate-ai-market-suggestions'
import { generateAIMarketSuggestions2 } from './generate-ai-market-suggestions-2'
import { generateAIDescription } from './generate-ai-description'
import { generateAIAnswers } from './generate-ai-answers'
import { getmonthlybets2024 } from './get-monthly-bets-2024'
import { getmaxminprofit2024 } from './get-max-min-profit-2024'
import { getNextLoanAmount } from './get-next-loan-amount'

// we define the handlers in this object in order to typecheck that every API has a handler
Expand Down Expand Up @@ -292,5 +294,7 @@ export const handlers: { [k in APIPath]: APIHandler<k> } = {
'generate-ai-market-suggestions-2': generateAIMarketSuggestions2,
'generate-ai-description': generateAIDescription,
'generate-ai-answers': generateAIAnswers,
'get-monthly-bets-2024': getmonthlybets2024,
'get-max-min-profit-2024': getmaxminprofit2024,
'get-next-loan-amount': getNextLoanAmount,
}
20 changes: 20 additions & 0 deletions common/src/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,26 @@ export const API = (_apiTypeCheck = {
})
.strict(),
},
'get-monthly-bets-2024': {
method: 'GET',
visibility: 'public',
authed: true,
props: z.object({ userId: z.string() }),
returns: [] as { month: string; bet_count: number; total_amount: number }[],
},
'get-max-min-profit-2024': {
method: 'GET',
visibility: 'public',
authed: true,
props: z.object({ userId: z.string() }),
returns: [] as {
profit: number
data: Contract
answer_id: string | null
has_no_shares: boolean
has_yes_shares: boolean
}[],
},
'get-next-loan-amount': {
method: 'GET',
visibility: 'undocumented',
Expand Down
3 changes: 2 additions & 1 deletion web/components/nav/profile-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ export function ProfileSummary(props: { user: User; className?: string }) {
<CoinNumber
amount={user?.balance}
numberType="animated"
className="mr-2 text-violet-600 dark:text-violet-400"
className="mr-1 text-violet-600 dark:text-violet-400"
/>
<span>🎁</span>
</div>
<CoinNumber
className="text-sm text-amber-600 dark:text-amber-400"
Expand Down
200 changes: 200 additions & 0 deletions web/components/wrapped/GeneralStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import clsx from 'clsx'
import { User } from 'common/user'
import { formatMoney } from 'common/util/format'
import { useEffect, useState } from 'react'
import { MonthlyBetsType } from 'web/hooks/use-wrapped-2024'
import { Spacer } from '../layout/spacer'
import { LoadingIndicator } from '../widgets/loading-indicator'
import { NavButtons } from './NavButtons'

export function GeneralStats(props: {
monthlyBets: MonthlyBetsType[] | undefined | null
goToPrevPage: () => void
goToNextPage: () => void
user: User
}) {
const { goToPrevPage, goToNextPage, monthlyBets } = props
const animateTotalSpentIn = true
const [animateMostSpentIn, setAnimateMostSpentIn] = useState(false)
const [animateGraphicIn, setAnimateGraphicIn] = useState(false)
const [animateOut, setAnimateOut] = useState(false)

//triggers for animation in
useEffect(() => {
if (!animateTotalSpentIn) return
const timeout1 = setTimeout(() => {
setAnimateMostSpentIn(true)
}, 1500)
const timeout2 = setTimeout(() => {
setAnimateGraphicIn(true)
}, 3000)
const timeout3 = setTimeout(() => {
onGoToNext()
}, 6000)
return () => {
clearTimeout(timeout1)
clearTimeout(timeout2)
clearTimeout(timeout3)
}
}, [animateTotalSpentIn])

const onGoToNext = () => {
setAnimateOut(true)
setTimeout(() => {
goToNextPage()
}, 1000)
}

if (monthlyBets == undefined) {
return (
<div className="mx-auto my-auto">
<LoadingIndicator />
</div>
)
}
const amountBetThisYear = monthlyBets.reduce((accumulator, current) => {
return accumulator + current.total_amount
}, 0)

if (monthlyBets == null) {
return <>An error occured</>
}

const monthWithMostBet = monthlyBets.reduce((max, current) => {
return current.total_amount > max.total_amount ? current : max
})
// Create a date object using the UTC constructor to prevent timezone offsets from affecting the month
const dateOfMostBet = new Date(monthWithMostBet.month)
dateOfMostBet.setDate(dateOfMostBet.getDate() + 1)

// Now you have the month with the highest number of bets
const monthName = dateOfMostBet.toLocaleString('default', {
month: 'long',
timeZone: 'UTC',
})

return (
<>
<div className="relative mx-auto my-auto max-w-lg overflow-hidden">
<div
className={clsx(
'px-4 text-2xl',
animateOut ? 'animate-fade-out' : 'animate-fade-in'
)}
>
This year you spent{' '}
<span className="font-bold text-purple-300">
{formatMoney(amountBetThisYear)}
</span>{' '}
trading on things you believed in!
</div>
<Spacer h={4} />
<div
className={clsx(
'px-4 text-2xl ',
animateMostSpentIn
? animateOut
? 'animate-fade-out'
: 'animate-fade-in'
: 'invisible'
)}
>
You traded the most in{' '}
<span className={clsx('highlight-black font-bold text-purple-300')}>
{monthName}
</span>
, spending{' '}
<span className="font-bold text-purple-300">
{formatMoney(monthWithMostBet.total_amount)}
</span>{' '}
mana!
</div>
<div
className={clsx(
animateGraphicIn
? animateOut
? 'animate-slide-right-out'
: 'animate-slide-right-in'
: 'invisible'
)}
>
<CoinBarChart data={monthlyBets} />
</div>
</div>
<NavButtons goToPrevPage={goToPrevPage} goToNextPage={onGoToNext} />
</>
)
}

const CoinBarChart = (props: { data: MonthlyBetsType[] }) => {
const { data } = props
const svgWidth = 280
const svgHeight = 350
const maxCoins = 20 // Maximum number of coins in a stack
const coinWidth = 9 // Width of the oval (coin)
const coinHeight = 3 // Height of the oval (coin)
const spacing = 35 // Horizontal spacing between stacks
const rowSpacing = svgHeight / 3 // Vertical spacing between rows

const maxManaBet = Math.max(...data.map((item) => item.total_amount))
const scaleFactor = maxManaBet > 0 ? maxCoins / maxManaBet : 1

return (
<div className="ml-6 sm:ml-20">
<svg width={svgWidth} height={svgHeight}>
{data.map((item, index) => {
const coinsInStack = Math.round(item.total_amount * scaleFactor)
const isTopRow = index < 6 // First 6 months (Jan-Jun) are in the top row
const rowIndex = isTopRow ? index : index - 6 // Adjust index for each row
const xPosition = (svgWidth / 6) * rowIndex + spacing // X position of each stack
const yBasePosition = isTopRow ? rowSpacing : rowSpacing * 2 // Y base position for each row

return (
<g key={index}>
{/* Stack of coins */}
{Array.from({ length: coinsInStack }).map((_, coinIndex) => {
const yPosition = yBasePosition - (coinIndex * coinHeight + 30)
return (
<ellipse
key={coinIndex}
cx={xPosition}
cy={yPosition}
rx={coinWidth}
ry={coinHeight}
fill="gold" // Change color as needed
stroke="#92400e"
strokeWidth="1"
/>
)
})}
{/* Month label */}
<text
x={xPosition - coinWidth}
y={yBasePosition}
fill="white"
fontSize="12"
>
{MONTHS[index]}
</text>
</g>
)
})}
</svg>
</div>
)
}

export const MONTHS = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'June',
'July',
'Aug',
'Sept',
'Oct',
'Nov',
'Dec',
]
Loading

0 comments on commit 6cdb616

Please sign in to comment.