-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,307 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
] |
Oops, something went wrong.