Calculate the tax, and show a table of all transactions
This commit is contained in:
parent
7ffef0294a
commit
432575ae41
|
@ -5,7 +5,11 @@ import { User } from '../../common/user'
|
|||
import { formatMoney } from '../../common/util/format'
|
||||
import { useUser } from '../hooks/use-user'
|
||||
import { buyLeaderboardSlot } from '../lib/firebase/api-call'
|
||||
import { Transaction, writeTransaction } from '../lib/firebase/transactions'
|
||||
import {
|
||||
SlotData,
|
||||
Transaction,
|
||||
writeTransaction,
|
||||
} from '../lib/firebase/transactions'
|
||||
import { AmountInput } from './amount-input'
|
||||
import { Avatar } from './avatar'
|
||||
import { Col } from './layout/col'
|
||||
|
@ -18,10 +22,11 @@ export function Manaboard(props: {
|
|||
title: string
|
||||
users: User[]
|
||||
values: number[]
|
||||
createdTimes: number[]
|
||||
className?: string
|
||||
}) {
|
||||
// TODO: Ideally, highlight your own entry on the leaderboard
|
||||
let { title, users, className, values } = props
|
||||
let { title, users, className, values, createdTimes } = props
|
||||
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
if (!expanded) {
|
||||
|
@ -67,6 +72,7 @@ export function Manaboard(props: {
|
|||
title={`${title}`}
|
||||
holder={user}
|
||||
value={values[index]}
|
||||
createdTime={createdTimes[index]}
|
||||
/>
|
||||
</Row>
|
||||
</td>
|
||||
|
@ -96,8 +102,9 @@ export function BuySlotModal(props: {
|
|||
holder: User
|
||||
slot: number
|
||||
value: number
|
||||
createdTime: number
|
||||
}) {
|
||||
const { slot, title, holder, value } = props
|
||||
const { slot, title, holder, value, createdTime } = props
|
||||
const user = useUser()
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
@ -109,10 +116,29 @@ export function BuySlotModal(props: {
|
|||
}
|
||||
}, [user])
|
||||
|
||||
const onBuy = async () => {
|
||||
// Feel free to change this. - James
|
||||
const slotId = `${title}-${slot}`
|
||||
await buyLeaderboardSlot({ slotId, reassessValue: newValue })
|
||||
// const onBuy = async () => {
|
||||
// // Feel free to change this. - James
|
||||
// const slotId = `${title}-${slot}`
|
||||
// await buyLeaderboardSlot({ slotId, reassessValue: newValue })
|
||||
// }
|
||||
|
||||
async function onBuy() {
|
||||
if (user) {
|
||||
// Start transactions, but don't block
|
||||
const buyData = { slot, newValue, message }
|
||||
const buyTxn = buyTransaction({
|
||||
buyer: user,
|
||||
holder,
|
||||
amount: value,
|
||||
buyData,
|
||||
})
|
||||
await Promise.all([
|
||||
writeTransaction(buyTxn),
|
||||
writeTransaction(taxTransaction({ holder, slot, value, createdTime })),
|
||||
])
|
||||
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -148,22 +174,7 @@ export function BuySlotModal(props: {
|
|||
label={ENV_CONFIG.moneyMoniker}
|
||||
/>
|
||||
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
if (user) {
|
||||
buySlot({
|
||||
holder,
|
||||
buyer: user,
|
||||
amount: value,
|
||||
slot,
|
||||
message,
|
||||
newValue,
|
||||
})
|
||||
setOpen(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button className="btn btn-primary" onClick={onBuy}>
|
||||
Buy Slot ({formatMoney(value)})
|
||||
</button>
|
||||
<div className="-mt-2 text-sm">
|
||||
|
@ -181,19 +192,16 @@ export function BuySlotModal(props: {
|
|||
)
|
||||
}
|
||||
|
||||
async function buySlot(options: {
|
||||
holder: User
|
||||
function buyTransaction(options: {
|
||||
buyer: User
|
||||
holder: User
|
||||
buyData: SlotData
|
||||
amount: number
|
||||
slot: number
|
||||
message: string
|
||||
newValue: number
|
||||
}) {
|
||||
const { holder, buyer, amount, slot, message, newValue } = options
|
||||
const createdTime = Date.now()
|
||||
const buyTransaction: Transaction = {
|
||||
}): Transaction {
|
||||
const { buyer, holder, buyData, amount } = options
|
||||
return {
|
||||
id: '',
|
||||
createdTime,
|
||||
createdTime: Date.now(),
|
||||
|
||||
fromId: buyer.id,
|
||||
fromName: buyer.name,
|
||||
|
@ -205,20 +213,30 @@ async function buySlot(options: {
|
|||
toUsername: holder.username,
|
||||
toAvatarUrl: holder.avatarUrl,
|
||||
|
||||
amount: amount,
|
||||
amount,
|
||||
|
||||
category: 'BUY_LEADERBOARD_SLOT',
|
||||
description: `${buyer.name} bought a slot from ${holder.name}`,
|
||||
data: {
|
||||
slot,
|
||||
message,
|
||||
newValue,
|
||||
},
|
||||
data: buyData,
|
||||
}
|
||||
}
|
||||
|
||||
const feeTransaction: Transaction = {
|
||||
function taxTransaction(options: {
|
||||
holder: User
|
||||
slot: number
|
||||
value: number
|
||||
createdTime: number
|
||||
}): Transaction {
|
||||
const { holder, slot, value, createdTime } = options
|
||||
|
||||
const APRIL_FOOLS_PT = 1648796400000
|
||||
const elapsedMs = Date.now() - (createdTime || APRIL_FOOLS_PT)
|
||||
const elapsedHours = elapsedMs / 1000 / 60 / 60
|
||||
const tax = elapsedHours * (value / 10)
|
||||
|
||||
return {
|
||||
id: '',
|
||||
createdTime,
|
||||
createdTime: Date.now(),
|
||||
|
||||
fromId: holder.id,
|
||||
fromName: holder.name,
|
||||
|
@ -231,16 +249,12 @@ async function buySlot(options: {
|
|||
toUsername: 'ManifoldMarkets',
|
||||
toAvatarUrl: 'https://manifold.markets/logo-bg-white.png',
|
||||
|
||||
amount: 10, // TODO: Calculate fee
|
||||
amount: tax,
|
||||
|
||||
category: 'LEADERBOARD_TAX',
|
||||
description: `${holder.name} paid M$ 10 in fees`,
|
||||
data: {
|
||||
slot,
|
||||
},
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
writeTransaction(buyTransaction),
|
||||
writeTransaction(feeTransaction),
|
||||
])
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@ import { Title } from '../components/title'
|
|||
import { useTransactions } from '../hooks/use-transactions'
|
||||
import { SlotData, Transaction } from '../lib/firebase/transactions'
|
||||
|
||||
import { Grid, _ as r } from 'gridjs-react'
|
||||
import 'gridjs/dist/theme/mermaid.css'
|
||||
import { html } from 'gridjs'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export const getStaticProps = fromPropz(getStaticPropz)
|
||||
export async function getStaticPropz() {
|
||||
const [topTraders, topCreators] = await Promise.all([
|
||||
|
@ -94,11 +99,12 @@ function Explanation() {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// TODOs
|
||||
// [ ] Correctly calculate tax
|
||||
// [ ] List history of purchases at the bottom
|
||||
// [ ] Restrict to at most buying one slot per user?
|
||||
// [ ] Deduct amount from user's balance, either in UX or for real
|
||||
// [ ] Draw attention to leaderboard
|
||||
// [ ] Show total returned to Manifold
|
||||
// [ ] Restrict to at most buying one slot per user?
|
||||
export default function Manaboards(props: {
|
||||
topTraders: User[]
|
||||
topCreators: User[]
|
||||
|
@ -112,6 +118,7 @@ export default function Manaboards(props: {
|
|||
const values = Array.from(Array(topTraders.length).keys())
|
||||
.map((i) => i + 1)
|
||||
.reverse()
|
||||
const createdTimes = new Array(topTraders.length).fill(0)
|
||||
|
||||
// Find the most recent purchases of each slot, and replace the entries in topTraders
|
||||
const transactions = useTransactions() ?? []
|
||||
|
@ -124,6 +131,7 @@ export default function Manaboards(props: {
|
|||
const slot = data.slot
|
||||
topTraders[slot - 1] = buyer
|
||||
values[slot - 1] = data.newValue
|
||||
createdTimes[slot - 1] = txn.createdTime
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,10 +169,68 @@ export default function Manaboards(props: {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<Col className="mt-6 items-center gap-10">
|
||||
<Manaboard title="" users={topTraders} values={values} />
|
||||
<Col className="mt-6 gap-10">
|
||||
<Manaboard
|
||||
title=""
|
||||
users={topTraders}
|
||||
values={values}
|
||||
createdTimes={createdTimes}
|
||||
/>
|
||||
{/* <Manaboard title="🏅 Top creators" users={topCreators} /> */}
|
||||
|
||||
<div className="text-sm">
|
||||
<Title text={'Transaction log'} />
|
||||
<TransactionsTable txns={_.reverse(sortedTxns)} />
|
||||
</div>
|
||||
</Col>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
function TransactionsTable(props: { txns: Transaction[] }) {
|
||||
const { txns } = props
|
||||
return (
|
||||
<Grid
|
||||
data={txns}
|
||||
search={true}
|
||||
// sort={true}
|
||||
pagination={{
|
||||
enabled: true,
|
||||
limit: 25,
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
id: 'data',
|
||||
name: 'Slot',
|
||||
formatter: (cell) => (cell as SlotData).slot,
|
||||
},
|
||||
{
|
||||
id: 'category',
|
||||
name: 'Type',
|
||||
formatter: (cell) =>
|
||||
cell === 'BUY_LEADERBOARD_SLOT' ? 'Buy' : 'Tax',
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
name: 'Amount',
|
||||
formatter: (cell) => formatMoney(cell as number),
|
||||
},
|
||||
{
|
||||
id: 'fromUsername',
|
||||
name: 'From',
|
||||
},
|
||||
{ id: 'toUsername', name: 'To' },
|
||||
{
|
||||
id: 'createdTime',
|
||||
name: 'Time',
|
||||
formatter: (cell) =>
|
||||
html(
|
||||
`<span class="whitespace-nowrap">${dayjs(cell as number).format(
|
||||
'h:mma'
|
||||
)}</span>`
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user