import { UserLedgerTransactionsResponse, UserLedgerTransactionsResponseTransactionsInner, WalletApi } from "@/api_fsb"; import { useAppDispatch, useAppSelector } from "@/hooks"; import { bpid, formatCentsToUSD, } from "@/lib/utils"; import { setTxPagination, setTxTotalItems, setTxTotalPages, setUserLedgerSummary, setUserLedgerTxs } from "@/models/appSlice"; import { RowData, createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; import moment from "moment"; import { useEffect, useState } from "react"; declare module '@tanstack/react-table' { interface ColumnMeta { align?: 'left' | 'center' | 'right'; } } // Function to calculate background color based on balance const getBackgroundColor = (balance: number | undefined): string => { if (balance === undefined || balance === null) return 'bg-blue-600'; if (balance > 0) { return 'bg-blue-600'; } else if (balance <= -100) { return 'bg-red-600'; } else { // Interpolate between blue and red for values between 0 and -100 // progress goes from 0 (at balance = 0) to 1 (at balance = -100) const progress = Math.abs(balance) / 100; // Blue RGB: (37, 99, 235) - Tailwind blue-600 // Red RGB: (220, 38, 38) - Tailwind red-600 const r = Math.round(37 + (220 - 37) * progress); const g = Math.round(99 + (38 - 99) * progress); const b = Math.round(235 + (38 - 235) * progress); return `rgb(${r}, ${g}, ${b})`; } }; const WalletHeader = () => { const amount = useAppSelector(state => state.app.userWalletBalance)?.amount ?? 0 const bgStyle = typeof getBackgroundColor(amount) === 'string' && getBackgroundColor(amount).startsWith('bg-') ? {} : { backgroundColor: getBackgroundColor(amount) }; const bgClass = typeof getBackgroundColor(amount) === 'string' && getBackgroundColor(amount).startsWith('bg-') ? getBackgroundColor(amount) : ''; return (

User Wallet: {amount >= 0 ? ( {`${formatCentsToUSD(Math.abs(amount))}`} ) : ( {`(${formatCentsToUSD(Math.abs(amount))})`} )}

) }; const WalletSummary = () => { const summary = useAppSelector(state => state.app.userLedgerSummary) const survey_completes = summary?.bp_payment?.entry_count ?? 0 const survey_completes_f = (survey_completes ?? 0).toLocaleString('en-US') const survey_adjustments = summary?.bp_adjustment?.entry_count ?? 0 const survey_adjustments_f = (survey_adjustments ?? 0).toLocaleString('en-US') const min_payout: number = summary?.bp_payment?.min_amount ?? 0 const max_payout: number = summary?.bp_payment?.max_amount ?? 0 const adj_total: number = summary?.bp_adjustment?.total_amount ?? 0 const user_payments_total: number = (summary?.user_payout_request?.total_amount ?? 0) * -1 return (
Completes:
{survey_completes_f} surveys
Avg Payouts:
{formatCentsToUSD(min_payout)} – {formatCentsToUSD(max_payout)}
Survey Adjustments:
{survey_adjustments_f}
Adjustment Amount:
{adj_total >= 0 ? ( {`${formatCentsToUSD(adj_total)}`} ) : ( {`(${formatCentsToUSD(Math.abs(adj_total))})`} )}
User Payments:
{user_payments_total >= 0 ? ( {`${formatCentsToUSD(user_payments_total)}`} ) : ( {`(${formatCentsToUSD(Math.abs(user_payments_total))})`} )}
) }; const WalletTransactionsHeader = () => { const tx_cnt = useAppSelector(state => state.app.txTotalItems) const txt_cnt_f = (tx_cnt ?? 0).toLocaleString('en-US') const [showTx, setShowTx] = useState(false); return (
setShowTx(!showTx)}>

setShowTx(!showTx)} > Total Transactions: {txt_cnt_f}

setShowTx(!showTx)} > (Click to {showTx ? 'hide' : 'show'})

{showTx && ( <> )}
) }; const WalletTransactions = () => { const dispatch = useAppDispatch() const bpuid = useAppSelector(state => state.app.bpuid) const userLedgerTxs = useAppSelector(state => state.app.userLedgerTxs); const txPagination = useAppSelector(state => state.app.txPagination); const txTotalPages = useAppSelector(state => state.app.txTotalPages); useEffect(() => { if (!bpuid) return; new WalletApi().getUserTransactionHistoryProductIdTransactionHistoryGet( bpid, // productId bpuid, // bpuid undefined, // createdAfter undefined, // createdBefore "-created", // orderBy txPagination.pageIndex + 1, // page txPagination.pageSize // pageSize ).then(res => { const response = res.data as UserLedgerTransactionsResponse; console.log("Wallet: fetched user ledger", response); dispatch(setUserLedgerSummary(response.summary)); dispatch(setUserLedgerTxs(response.transactions ?? [])) dispatch(setTxTotalItems(response.total!)) dispatch(setTxTotalPages(response.pages!)) }); }, [bpuid, txPagination.pageIndex, txPagination.pageSize]); const columnHelper = createColumnHelper() const columns = [ columnHelper.accessor('created', { header: () => 'Date', cell: (info) => moment(info.getValue()).format('M/D/YY H:mm'), size: 110, meta: { align: 'left' } }), columnHelper.accessor('description', { header: () => 'Type', cell: props => { const desc = props.getValue(); switch (desc) { case 'Task Complete': return 'Survey 🎉'; case 'Task Adjustment': return 'Reject ⚠️'; case 'Compensation Bonus': return 'Bonus 🎁'; case 'HIT Bonus': return 'Bonus'; case 'HIT Reward': return 'Assignment'; default: return desc; } }, size: 80, meta: { align: 'left' } }), columnHelper.accessor('amount', { header: () => 'Amount', cell: (props) => { const val = props.renderValue() as number; const isPositive = val >= 0; const emoji = isPositive ? "⬆\uFE0E" : "⬇\uFE0E"; const colorClass = isPositive ? 'font-variant-emoji-text text-emerald-300' : 'font-variant-emoji-text text-rose-300'; return ( {`${emoji} ${formatCentsToUSD(Math.abs(val))}`} ) }, size: 70, meta: { align: 'center' } }), columnHelper.accessor('balance_after', { header: () => 'Balance', cell: (props) => { const val = props.renderValue() as number; const isPositive = val >= 0; const colorClass = isPositive ? 'text-emerald-300' : 'text-rose-300'; return ( <> {isPositive ? ( {`${formatCentsToUSD(Math.abs(val))}`} ) : ( {`(${formatCentsToUSD(Math.abs(val))})`} )} ) }, size: 70, meta: { align: 'center' } }), ] const table = useReactTable({ 'data': userLedgerTxs, 'columns': columns, getCoreRowModel: getCoreRowModel(), manualPagination: true, pageCount: txTotalPages, onPaginationChange: (updater) => { dispatch(setTxPagination( typeof updater === 'function' ? updater(txPagination) : updater )); }, state: { pagination: txPagination, }, }); return (
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { const align = header.column.columnDef.meta?.align || 'left'; const alignClass = align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'; return ( ) })} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => { const align = cell.column.columnDef.meta?.align || 'left'; const alignClass = align === 'center' ? 'text-center' : align === 'right' ? 'text-right' : 'text-left'; return ( ) })} ))}
{flexRender( header.column.columnDef.header, header.getContext(), )}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{/* Page info */}
Page {table.getState().pagination.pageIndex + 1} of{' '} {table.getPageCount()}
{/* Navigation buttons */}
{/* Page size selector */}
) }; const Wallet = () => { const dispatch = useAppDispatch() const bpuid = useAppSelector(state => state.app.bpuid) const pagination = useAppSelector(state => state.app.txPagination); useEffect(() => { if (!bpuid) return; new WalletApi().getUserTransactionHistoryProductIdTransactionHistoryGet( bpid, // productId bpuid, // bpuid undefined, // createdAfter undefined, // createdBefore "-created", // orderBy pagination.pageIndex + 1, // page pagination.pageSize // pageSize ).then(res => { const response = res.data as UserLedgerTransactionsResponse; dispatch(setUserLedgerSummary(response.summary)); dispatch(setUserLedgerTxs(response.transactions ?? [])) dispatch(setTxPagination({ pageIndex: (response.page ?? 1) - 1, pageSize: response.size ?? 10, })) dispatch(setTxTotalItems(response.total ?? 0)) dispatch(setTxTotalPages(response.pages ?? 9)) }); }, [bpuid]) if (!bpuid) return null; return (
) }; export default Wallet;