import { UserLedgerTransactionsResponse, UserLedgerTransactionsResponseTransactionsInner, WalletApi, } from "@/api_fsb"; import { useGetMeQuery } from "@/models/authAPI"; 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.userDisplayedWalletBalance?.amount) ?? 0; const amount_string = useAppSelector( (state) => state.app.userDisplayedWalletBalance?.amount_string, ) ?? "$0.00"; 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 ? ( {`${amount_string}`} ) : ( {`(${amount_string})`} )}

); }; const WalletSummary = () => { const summary = useAppSelector((state) => state.app.userLedgerSummary); const userWallet = useAppSelector((state) => state.app.userWallet); userWallet; 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 { data: user } = useGetMeQuery(); const userLedgerTxs = useAppSelector((state) => state.app.userLedgerTxs); const txPagination = useAppSelector((state) => state.app.txPagination); const txTotalPages = useAppSelector((state) => state.app.txTotalPages); useEffect(() => { if (!user?.product_user_id) return; new WalletApi() .getUserTransactionHistoryProductIdTransactionHistoryGet( bpid, // productId user.product_user_id, 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!)); }); }, [user?.product_user_id, 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 pagination = useAppSelector((state) => state.app.txPagination); const { data: user } = useGetMeQuery(); useEffect(() => { if (!user?.product_user_id) return; new WalletApi() .getUserTransactionHistoryProductIdTransactionHistoryGet( bpid, // productId user.product_user_id, 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)); }); }, [user?.product_user_id]); if (!user?.product_user_id) return null; return (
); }; export default Wallet;