Skip to content

Commit 3405977

Browse files
committed
Add Transactions screen and API endpoint
1 parent 4da63fe commit 3405977

File tree

9 files changed

+240
-1
lines changed

9 files changed

+240
-1
lines changed
+11
Loading

public/icons/other/no_data.jpg

21.2 KB
Loading
Loading
+3
Loading

src/App.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import Profile from './screens/Profile/Profile'
3232
import Test from './screens/Test'
3333
import Video from './screens/Video'
3434
import Wallet from './screens/Wallet/Wallet'
35+
import Transactions from './screens/Transactions/Transactions'
3536

3637
const LiveVideo = lazyWithPreload(() => import('./screens/Live/LiveVideo'))
3738
const OrderStatus = lazyWithPreload(() => import('./screens/OrderStatus/OrderStatus'))
@@ -97,6 +98,11 @@ const router = createBrowserRouter([
9798
path: 'wallet',
9899
element: <Wallet />,
99100
},
101+
102+
{
103+
path: 'transaction-history',
104+
element: <Transactions />,
105+
},
100106
{
101107
path: 'creator/:creator',
102108
element: <Creator />,

src/lib/api.ts

+13
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,26 @@ const API = {
106106
wallet: {
107107
get_coins_list: `${API_URL}/wallet/GetCoinBundle`,
108108
buy_coins: `${API_URL}/payment/BuyCoins`,
109+
transactions: `${API_URL}/wallet/TransactionsFetch`,
109110
},
110111
}
111112

112113
export default API
113114

114115
// All API calls
115116

117+
export async function getTransactions_f(page: number): Promise<apiResponse> {
118+
try {
119+
const res = await fetch(API.wallet.transactions + '?page=' + page, {
120+
method: 'POST',
121+
headers: authorizedHeader(defaultHeaders),
122+
})
123+
return await returnResponse(res)
124+
} catch (err) {
125+
return catchError(err)
126+
}
127+
}
128+
116129
export async function cancelSubscription_f(): Promise<apiResponse> {
117130
try {
118131
const res = await fetch(API.subscription.cancel, {

src/screens/Profile/utils.ts

-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,5 @@ export async function updateLocalUserData() {
6060
if (userProfileData.status) {
6161
setProfileInfoLs(userProfileData.data)
6262
store.dispatch(setProfile(userProfileData.data as UserProfile))
63-
console.log(userProfileData)
6463
}
6564
}
+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { SmallCoin } from '@/components/Coin'
2+
import { Header } from '@/components/Header/Header'
3+
import TapMotion from '@/components/TapMotion'
4+
import { usePopupAlertContext } from '@/context/PopupAlertContext'
5+
import { getTransactions_f } from '@/lib/api'
6+
import icon from '@/lib/icons'
7+
import transitions from '@/lib/transition'
8+
import { useEffect, useRef, useState } from 'react'
9+
10+
export interface TransactionType {
11+
reference_id: string
12+
coins: string
13+
transaction_type: 'credit' | 'debit'
14+
description: string
15+
created_at: string
16+
}
17+
18+
export default function Transactions() {
19+
const [page, setPage] = useState(1)
20+
const [transactions, setTransactions] = useState<TransactionType[] | null>(null)
21+
const observerTarget = useRef<HTMLDivElement>(null)
22+
const [isLoading, setIsLoading] = useState(false)
23+
const [isMorePageAvailable, setIsMorePageAvailable] = useState(true)
24+
25+
async function loadTransactions(page: number) {
26+
setIsLoading(true)
27+
console.log('Loading Page ', page)
28+
const transactionStatus = await getTransactions_f(page)
29+
if (!transactionStatus.status) return
30+
31+
if (transactionStatus.data.data.next_page_url === null) {
32+
setIsMorePageAvailable(false)
33+
}
34+
35+
// Set Transactions
36+
const newTransactions = transactionStatus.data.data.data
37+
const allTransactions = transactions ? [...transactions, ...newTransactions] : newTransactions
38+
setTransactions(allTransactions)
39+
setIsLoading(false)
40+
}
41+
42+
useEffect(() => {
43+
const observer = new IntersectionObserver(
44+
(entries) => {
45+
if (entries[0].isIntersecting) {
46+
// Load More Data
47+
if (isLoading) return
48+
setIsLoading(true)
49+
loadTransactions(page + 1)
50+
setPage(page + 1)
51+
}
52+
},
53+
{ threshold: 1 },
54+
)
55+
56+
if (observerTarget.current) {
57+
observer.observe(observerTarget.current)
58+
}
59+
60+
return () => {
61+
if (observerTarget.current) {
62+
observer.unobserve(observerTarget.current)
63+
}
64+
}
65+
}, [observerTarget, isLoading])
66+
67+
useEffect(() => {
68+
loadTransactions(page)
69+
}, [])
70+
71+
return (
72+
<>
73+
<Header>
74+
<span className='font-[450]'>Transaction History</span>
75+
</Header>
76+
<div className='p-5 pt-0'>
77+
<div>
78+
<AllTransactions transactions={transactions} />
79+
<div className='mt-3 flex w-full items-center justify-center'>
80+
{isMorePageAvailable ? (
81+
isLoading ? (
82+
<div className='tap95 highlight-none font-normMid mt-5 animate-pulse rounded-full px-7 text-xs'>
83+
<img src={icon('linear_loading_dots.svg')} className='h-10 opacity-50 dark:invert' />
84+
</div>
85+
) : null
86+
) : transactions?.length ? (
87+
<span className='font-normMid mt-5 text-xs opacity-50'>No More Transactions</span>
88+
) : null}
89+
</div>
90+
{isMorePageAvailable && <div ref={observerTarget}></div>}
91+
</div>
92+
</div>
93+
</>
94+
)
95+
}
96+
97+
function TransactionDetails({ transaction }: { transaction: TransactionType }) {
98+
return (
99+
<div className='flex flex-col gap-2'>
100+
<span className='font-normMid'>{transaction.description}</span>
101+
<span>
102+
Transaction of <span className='font-normMid'>{transaction.coins} Trapp coins</span> on{' '}
103+
<span>{niceDate(transaction.created_at)}</span>. <span>Your Transaction Reference is </span>{' '}
104+
<span className='select-all text-blue-500'>{transaction.reference_id}</span>.{' '}
105+
</span>
106+
</div>
107+
)
108+
}
109+
110+
function AllTransactions({ transactions }: { transactions: TransactionType[] | null }) {
111+
const { newPopup } = usePopupAlertContext()
112+
113+
if (transactions === null) {
114+
return <TransactionsShimmer />
115+
}
116+
117+
if (transactions.length === 0) {
118+
return <NoTransactions />
119+
}
120+
121+
return (
122+
<div className='mt-3 flex min-h-[40dvh] flex-col gap-3'>
123+
{transactions?.map((transaction, index) => (
124+
<TapMotion
125+
size='lg'
126+
className='bg-inputBg flex items-center justify-center gap-3 rounded-3xl p-4 dark:bg-white/10'
127+
key={index}
128+
onClick={transitions(() => {
129+
newPopup({
130+
title: `Transaction Details`,
131+
subTitle: <TransactionDetails transaction={transaction} />,
132+
action: [
133+
{
134+
text: 'OK',
135+
},
136+
],
137+
})
138+
})}
139+
>
140+
<img
141+
src={
142+
transaction.transaction_type === 'credit' ? icon('transaction/receive.svg') : icon('transaction/send.svg')
143+
}
144+
className='w-8'
145+
/>
146+
147+
<div className='flex flex-grow flex-col gap-0.5'>
148+
<p className='font-420 text-sm'>{transaction.description}</p>
149+
<p className='text-xs opacity-70'>{niceDate(transaction.created_at)}</p>
150+
</div>
151+
<div className='flex flex-col items-center justify-center gap-2 pr-1 text-right'>
152+
<SmallCoin />
153+
<p
154+
className={`font-normMid text-xs ${
155+
transaction.transaction_type === 'credit' ? 'text-green-500' : 'text-red-500'
156+
}`}
157+
>
158+
{transaction.transaction_type === 'credit' ? '+' : '-'}
159+
{transaction.coins}
160+
</p>
161+
</div>
162+
</TapMotion>
163+
))}
164+
</div>
165+
)
166+
}
167+
168+
function NoTransactions() {
169+
return (
170+
<div className='w-full'>
171+
<img src={icon('no_data.jpg')} className='w-full dark:grayscale dark:invert' />
172+
<p className='font-normMid text-center text-xs opacity-50'>No Transactions</p>
173+
</div>
174+
)
175+
}
176+
177+
function TransactionsShimmer() {
178+
return (
179+
<div className='flex min-h-[20dvh] items-end justify-center'>
180+
<p className='text-xs opacity-70'>Loading Transactions</p>
181+
</div>
182+
)
183+
}
184+
185+
// function getStatusColor(status: 'pending' | 'failed' | 'success' | 'refund') {
186+
// if (status === 'pending') return 'text-yellow-500'
187+
// if (status === 'failed') return 'text-red-500'
188+
// if (status === 'success') return 'text-green-500'
189+
// if (status === 'refund') return 'text-red-500'
190+
// return 'text-red-500'
191+
// }
192+
193+
function niceDate(date: string) {
194+
const d = new Date(date)
195+
return `${d.getDate()} ${d.toLocaleString('default', { month: 'short' })} ${d.getFullYear()}, ${d.toLocaleTimeString(
196+
'en-US',
197+
{
198+
hour: 'numeric',
199+
minute: 'numeric',
200+
hour12: true,
201+
},
202+
)}`
203+
}

src/screens/Wallet/Wallet.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ function AddCoinBox({ count, price, id }: { count: number; price: number; id: nu
7373
const res = await buyCoins_f(id.toString())
7474
if (!res) return
7575
if (res?.data?.payment_link) window.open(res.data.payment_link, '_blank')
76+
setLoading(false)
7677
}
7778

7879
return (

0 commit comments

Comments
 (0)