Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
sweepstake
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Arjun Jhukal
sweepstake
Commits
9382c249
Commit
9382c249
authored
Oct 28, 2025
by
Arjun Jhukal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
updated the filter by game functionality at admin transaction table
parent
189f6192
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
148 additions
and
111 deletions
+148
-111
SortableHeader.tsx
src/components/atom/SortableHeader.tsx
+17
-0
TableHeader.tsx
src/components/molecules/TableHeader.tsx
+4
-0
Filter.tsx
src/components/organism/Filter.tsx
+79
-68
TransactionTable.tsx
...dashboard/adminDashboard/transaction/TransactionTable.tsx
+44
-41
transaction.ts
src/services/transaction.ts
+4
-2
No files found.
src/components/atom/SortableHeader.tsx
0 → 100644
View file @
9382c249
import
{
ArrowUp
,
ArrowDown
}
from
'@wandersonalwes/iconsax-react'
;
export
default
function
SortableHeader
({
column
,
label
}:
{
column
:
any
;
label
:
string
})
{
const
sortState
=
column
.
getIsSorted
();
// asc | desc | false
const
arrow
=
sortState
===
"asc"
?
<
ArrowUp
size=
{
14
}
/>
:
sortState
===
"desc"
?
<
ArrowDown
size=
{
14
}
/>
:
null
;
return
(
<
p
onClick=
{
()
=>
column
.
toggleSorting
()
}
className=
"flex items-center gap-1 cursor-pointer"
>
{
label
}
{
arrow
}
</
p
>
);
};
src/components/molecules/TableHeader.tsx
View file @
9382c249
...
...
@@ -3,6 +3,7 @@ import { Button, IconButton, InputAdornment, OutlinedInput, useMediaQuery } from
import
SelectField
from
"../atom/SelectField"
;
import
{
DocumentDownload
,
SearchNormal
}
from
"@wandersonalwes/iconsax-react"
;
import
React
from
"react"
;
import
Filter
from
"../organism/Filter"
;
interface
FilterOption
{
value
:
string
;
...
...
@@ -101,6 +102,8 @@ export default function TableHeader({
/>
)
}
{
/* <Filter /> */
}
{
/* Download Button */
}
{
onDownloadCSV
&&
<
Button
startIcon=
{
!
downMD
&&
<
DocumentDownload
size=
{
16
}
/>
}
...
...
@@ -116,6 +119,7 @@ export default function TableHeader({
{
downMD
?
<
DocumentDownload
size=
{
16
}
/>
:
downloading
?
"Downloading..."
:
"Download CSV"
}
</
Button
>
}
</
div
>
</
div
>
);
}
src/components/organism/Filter.tsx
View file @
9382c249
"use client"
;
import
React
,
{
useRef
,
useState
}
from
"react"
;
import
{
Box
,
...
...
@@ -11,12 +12,12 @@ import {
List
,
ListItem
,
Divider
,
Dialog
,
DialogContent
,
}
from
"@mui/material"
;
import
{
ArrowDown2
}
from
"@wandersonalwes/iconsax-react"
;
import
{
LocalizationProvider
}
from
"@mui/x-date-pickers/LocalizationProvider"
;
import
{
AdapterDayjs
}
from
"@mui/x-date-pickers/AdapterDayjs"
;
import
dayjs
,
{
Dayjs
}
from
"dayjs"
;
import
CustomDateRangePicker
from
"../molecules/DateRangePicker"
;
import
CustomDateRangePicker
from
'../molecules/DateRangePicker'
;
interface
FilterOption
{
label
:
string
;
...
...
@@ -24,11 +25,11 @@ interface FilterOption {
}
interface
FilterProps
{
option
:
FilterOption
[];
currentFilter
:
number
|
null
;
setFilterDays
:
React
.
Dispatch
<
React
.
SetStateAction
<
number
|
null
>>
;
customRange
:
{
startDate
:
string
;
endDate
:
string
};
setCustomRange
:
React
.
Dispatch
<
option
?
:
FilterOption
[];
currentFilter
?
:
number
|
null
;
setFilterDays
?
:
React
.
Dispatch
<
React
.
SetStateAction
<
number
|
null
>>
;
customRange
?
:
{
startDate
:
string
;
endDate
:
string
};
setCustomRange
?
:
React
.
Dispatch
<
React
.
SetStateAction
<
{
startDate
:
string
;
endDate
:
string
}
>
>
;
}
...
...
@@ -42,12 +43,12 @@ export default function Filter({
}:
FilterProps
)
{
const
anchorRef
=
useRef
<
HTMLButtonElement
|
null
>
(
null
);
const
[
open
,
setOpen
]
=
useState
(
false
);
const
[
customRangeOpen
,
setCustomRangeOpen
]
=
useState
(
false
);
const
[
showCustomRangeModal
,
setShowCustomRangeModal
]
=
useState
(
false
);
const
[
startDate
,
setStartDate
]
=
useState
<
Dayjs
|
null
>
(
customRange
.
startDate
?
dayjs
(
customRange
.
startDate
)
:
null
customRange
?
.
startDate
?
dayjs
(
customRange
.
startDate
)
:
null
);
const
[
endDate
,
setEndDate
]
=
useState
<
Dayjs
|
null
>
(
customRange
.
endDate
?
dayjs
(
customRange
.
endDate
)
:
null
customRange
?
.
endDate
?
dayjs
(
customRange
.
endDate
)
:
null
);
const
handleToggle
=
()
=>
setOpen
((
prev
)
=>
!
prev
);
...
...
@@ -58,31 +59,44 @@ export default function Filter({
};
const
handleSelect
=
(
value
:
number
|
null
,
label
:
string
)
=>
{
setCustomRange
({
startDate
:
""
,
endDate
:
""
});
setStartDate
(
null
);
setEndDate
(
null
);
setFilterDays
(
value
);
setOpen
(
false
);
if
(
label
===
"Custom range"
)
{
setOpen
(
false
);
setShowCustomRangeModal
(
true
);
}
else
{
// Reset custom range when selecting predefined filters
setStartDate
(
null
);
setEndDate
(
null
);
setCustomRange
&&
setCustomRange
({
startDate
:
""
,
endDate
:
""
});
setFilterDays
&&
setFilterDays
(
value
);
setOpen
(
false
);
}
};
const
handleApplyCustomRange
=
()
=>
{
if
(
startDate
&&
endDate
)
{
setCustomRange
({
setCustomRange
&&
setCustomRange
({
startDate
:
startDate
.
format
(
"YYYY-MM-DD"
),
endDate
:
endDate
.
format
(
"YYYY-MM-DD"
),
});
setFilterDays
(
null
);
set
CustomRangeOpen
(
false
);
setFilterDays
&&
setFilterDays
(
null
);
set
ShowCustomRangeModal
(
false
);
}
};
const
handleResetCustomRange
=
()
=>
{
setStartDate
(
null
);
setEndDate
(
null
);
setShowCustomRangeModal
(
false
);
};
const
id
=
open
?
"filter-popper"
:
undefined
;
// Determine if custom range is active
const
isCustomRangeActive
=
customRange
?.
startDate
&&
customRange
?.
endDate
;
return
(
<
Box
>
{
/* 🔽 Filter Button */
}
<
IconButton
aria
-
describedby=
{
id
}
ref=
{
anchorRef
}
...
...
@@ -95,6 +109,7 @@ export default function Filter({
</
Typography
>
</
IconButton
>
{
/* Filter Dropdown */
}
<
Popper
id=
{
id
}
open=
{
open
}
...
...
@@ -115,26 +130,33 @@ export default function Filter({
>
<
ClickAwayListener
onClickAway=
{
handleClose
}
>
<
Box
className=
"p-3"
>
<
List
className=
"!p-0
mb-3
"
>
{
option
.
map
((
item
)
=>
(
<
List
className=
"!p-0"
>
{
option
&&
option
.
map
((
item
)
=>
(
<
ListItem
key=
{
item
.
label
}
className=
{
`!px-3 !py-1.5 text-sm rounded-md cursor-pointer ${currentFilter === item.value
? "bg-[#652CA0] text-white"
: ""
}`
}
onClick=
{
()
=>
handleSelect
(
item
.
value
,
item
.
label
)
}
onClick=
{
()
=>
handleSelect
(
item
.
value
,
item
.
label
)
}
>
{
item
.
label
}
</
ListItem
>
))
}
<
Divider
sx=
{
{
my
:
1
}
}
/>
<
ListItem
onClick=
{
()
=>
{
setOpen
(
false
);
setCustomRangeOpen
(
true
);
}
}
className=
"!px-3 !py-1.5 text-sm rounded-md cursor-pointer"
onClick=
{
()
=>
handleSelect
(
null
,
"Custom range"
)
}
className=
{
`!px-3 !py-1.5 text-sm rounded-md cursor-pointer ${isCustomRangeActive
? "bg-[#652CA0] text-white"
: ""
}`
}
>
Custom range
</
ListItem
>
...
...
@@ -146,44 +168,32 @@ export default function Filter({
)
}
</
Popper
>
<
Popper
open=
{
customRangeOpen
}
anchorEl=
{
anchorRef
.
current
}
placement=
"bottom-end"
transition
{
/* Custom Range Modal */
}
<
Dialog
open=
{
showCustomRangeModal
}
onClose=
{
()
=>
setShowCustomRangeModal
(
false
)
}
maxWidth=
"xs"
fullWidth
PaperProps=
{
{
sx
:
{
borderRadius
:
3
,
backgroundColor
:
"transparent"
,
boxShadow
:
"none"
,
padding
:
"20px"
,
},
}
}
>
{
({
TransitionProps
})
=>
(
<
Fade
{
...
TransitionProps
}
timeout=
{
300
}
>
<
Paper
elevation=
{
3
}
sx=
{
{
borderRadius
:
3
,
mt
:
1
}
}
>
<
ClickAwayListener
onClickAway=
{
(
event
)
=>
{
// Ignore clicks on MUI Select menus (rendered in Portal)
if
((
event
.
target
as
HTMLElement
).
closest
(
'.MuiPopover-root, .MuiBox-root,.MuiInputBase-root'
))
{
return
;
}
setCustomRangeOpen
(
false
);
}
}
>
<
Box
className=
"p-3 w-[300px]"
>
<
LocalizationProvider
dateAdapter=
{
AdapterDayjs
}
>
<
CustomDateRangePicker
startDate=
{
startDate
}
endDate=
{
endDate
}
onStartDateChange=
{
setStartDate
}
onEndDateChange=
{
setEndDate
}
onApply=
{
handleApplyCustomRange
}
onReset=
{
()
=>
{
setStartDate
(
null
);
setEndDate
(
null
);
setCustomRangeOpen
(
false
);
}
}
/>
</
LocalizationProvider
>
</
Box
>
</
ClickAwayListener
>
</
Paper
>
</
Fade
>
)
}
</
Popper
>
<
DialogContent
sx=
{
{
p
:
0
}
}
>
<
CustomDateRangePicker
startDate=
{
startDate
}
endDate=
{
endDate
}
onStartDateChange=
{
setStartDate
}
onEndDateChange=
{
setEndDate
}
onApply=
{
handleApplyCustomRange
}
onReset=
{
handleResetCustomRange
}
/>
</
DialogContent
>
</
Dialog
>
</
Box
>
);
}
}
\ No newline at end of file
src/components/pages/dashboard/adminDashboard/transaction/TransactionTable.tsx
View file @
9382c249
"use client"
;
import
SortableHeader
from
'@/components/atom/SortableHeader'
;
import
TableHeader
from
'@/components/molecules/TableHeader'
;
import
CustomTable
from
'@/components/organism/Table'
;
import
{
useAppDispatch
}
from
'@/hooks/hook'
;
import
{
useDownloadTransactionMutation
}
from
'@/services/downloadApi'
;
import
{
useGetAllGamesQuery
}
from
'@/services/gameApi'
;
import
{
useGetAllTransactionQuery
}
from
'@/services/transaction'
;
import
{
showToast
,
ToastVariant
}
from
'@/slice/toastSlice'
;
import
{
StatusOptions
}
from
'@/types/config'
;
...
...
@@ -21,6 +23,8 @@ import { ArrowDown, ArrowUp } from '@wandersonalwes/iconsax-react';
import
React
,
{
useMemo
,
useState
}
from
'react'
;
export
type
TransactionStatusProps
=
"success"
|
"failed"
|
"pending"
;
export
type
TransactionTypeProps
=
"deposit"
|
"withdrawl"
;
export
default
function
TransactionTable
({
user_id
,
game_id
,
search
,
setSearch
}:
{
user_id
?:
string
;
game_id
?:
number
,
search
:
string
,
setSearch
?:
(
newvalue
:
string
)
=>
void
})
{
const
dispatch
=
useAppDispatch
();
...
...
@@ -30,6 +34,8 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
const
[
pageSize
,
setPageSize
]
=
useState
(
10
);
const
[
rowSelection
,
setRowSelection
]
=
useState
({});
const
[
status
,
setStatus
]
=
React
.
useState
<
TransactionStatusProps
|
undefined
>
();
const
[
selectedGame
,
setSelectedGame
]
=
React
.
useState
(
""
);
const
[
selectedTransactionType
,
setSelectedTransationType
]
=
React
.
useState
<
TransactionTypeProps
|
string
>
(
""
);
const
queryArgs
=
useMemo
(
()
=>
({
page
,
...
...
@@ -37,9 +43,11 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
search
:
search
||
""
,
game_id
,
user_id
,
status
status
,
selectedGame
,
selectedTransactionType
}),
[
page
,
pageSize
,
search
,
game_id
,
user_id
,
status
]
[
page
,
pageSize
,
search
,
game_id
,
user_id
,
status
,
selectedGame
,
selectedTransactionType
]
);
const
{
data
,
isLoading
:
loadingTransaction
}
=
useGetAllTransactionQuery
(
queryArgs
);
...
...
@@ -50,43 +58,21 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
const
columns
=
useMemo
<
ColumnDef
<
SingleDepositProps
>
[]
>
(()
=>
[
{
accessorKey
:
"id"
,
header
:
({
column
})
=>
{
// Determine arrow: show Asc by default if not sorted
const
sortState
=
column
.
getIsSorted
();
const
arrow
=
sortState
===
"asc"
||
sortState
===
null
?
(
<
ArrowUp
size=
{
14
}
/>
)
:
sortState
===
"desc"
?
(
<
ArrowDown
size=
{
14
}
/>
)
:
null
;
return
(
<
p
onClick=
{
()
=>
column
.
toggleSorting
()
}
className=
"flex items-center gap-1 cursor-pointer"
>
#ID
{
arrow
}
</
p
>
);
},
// cell:({row})=>(
// <span className="text-center">{row.original.id}</span>
// )
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"#ID"
/>,
},
{
accessorKey
:
"name"
,
header
:
"Player Name"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Player Name"
/>
,
cell
:
({
row
})
=>
{
const
{
first_name
,
last_name
}
=
row
.
original
;
const
initials
=
getInitials
(
first_name
,
last_name
);
return
(
<
Box
className=
"flex
justify-start
items-center gap-2"
>
<
Box
className=
"flex items-center gap-2"
>
<
small
className=
"text-[10px] w-[24px] h-[24px] flex items-center justify-center uppercase rounded-[4px] bg-[#1EB41B]/10 font-[500] text-[#1EB41B]"
>
{
initials
}
</
small
>
<
div
className=
"name-detail"
>
<
strong
className=
"text-primary
block text-[12px] leading-[120%
] font-[500] capitalize"
>
<
div
>
<
strong
className=
"text-primary
text-[12px
] font-[500] capitalize"
>
{
first_name
}
{
last_name
}
</
strong
>
<
small
className=
"text-[10px] text-para-light font-[500]"
>
...
...
@@ -99,39 +85,34 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
},
{
accessorKey
:
"method"
,
header
:
"Method"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Method"
/>
,
},
{
accessorKey
:
"game_name"
,
header
:
"Game Name"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Game Name"
/>
,
},
{
accessorKey
:
"type"
,
header
:
"Type"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Type"
/>
,
cell
:
({
row
})
=>
{
const
status
=
row
.
original
.
status
.
toLowerCase
();
const
display
=
status
.
charAt
(
0
).
toUpperCase
()
+
status
.
slice
(
1
);
return
(
<
span
className=
{
`px-2 py-1 max-w-[60px] block lg:text-[10px] text-white status rounded-[8px] text-center ${status}`
}
>
{
display
}
</
span
>
<
span
className=
{
`px-2 py-1 max-w-[60px] block lg: text-[10px] text-white status rounded-[8px] text-center ${status}`
}
>
{
display
}
</
span
>
);
},
},
{
accessorKey
:
"amount"
,
header
:
"Amount USD"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Amount USD"
/>
,
},
{
accessorKey
:
"sweepcoins"
,
header
:
"Sweepcoins"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Sweepcoins"
/>
,
},
{
accessorKey
:
"transaction_date"
,
header
:
"Transaction Date"
,
header
:
({
column
})
=>
<
SortableHeader
column=
{
column
}
label=
"Transaction Date"
/>
,
cell
:
({
row
})
=>
{
const
{
date
,
time
}
=
formatDateTime
(
row
.
original
.
transaction_date
as
string
);
return
(
...
...
@@ -144,6 +125,7 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
},
],
[]);
const
table
=
useReactTable
({
data
:
tableData
,
columns
,
...
...
@@ -156,6 +138,8 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
// onRowSelectionChange: setRowSelection,
});
const
{
data
:
games
,
isLoading
}
=
useGetAllGamesQuery
();
return
(
<
div
className=
"border-gray border-solid border-[1px] rounded-[8px] lg:rounded-[16px]"
>
...
...
@@ -230,6 +214,25 @@ export default function TransactionTable({ user_id, game_id, search, setSearch }
options
:
StatusOptions
,
placeholder
:
"Filter by status"
,
},
{
value
:
selectedGame
||
""
,
setValue
:
(
value
)
=>
setSelectedGame
(
value
as
string
),
options
:
games
?.
data
?.
data
.
map
((
game
)
=>
({
label
:
game
.
name
,
value
:
game
.
id
.
toString
(),
}))
||
[],
placeholder
:
"Filter by Game"
,
},
{
value
:
selectedTransactionType
||
""
,
setValue
:
(
value
)
=>
setSelectedTransationType
(
value
as
string
),
options
:
[
{
label
:
"All"
,
value
:
""
},
{
label
:
"Withdrawn"
,
value
:
"withdrawn"
},
{
label
:
"Deposit"
,
value
:
"deposit"
},
],
placeholder
:
"Filter by Transaction Type"
,
},
]
}
/>
...
...
src/services/transaction.ts
View file @
9382c249
...
...
@@ -60,8 +60,8 @@ export const transactionApi = createApi({
},
providesTags
:
[
"Withdrawl"
]
}),
getAllTransaction
:
builder
.
query
<
DepositListProps
,
QueryParams
&
{
status
?:
TransactionStatusProps
;
user_id
?:
string
|
number
;
game_id
?:
string
|
number
}
>
({
query
:
({
search
,
page
,
per_page
,
user_id
,
game_id
,
status
})
=>
{
getAllTransaction
:
builder
.
query
<
DepositListProps
,
QueryParams
&
{
status
?:
TransactionStatusProps
;
user_id
?:
string
|
number
;
game_id
?:
string
|
number
,
selectedGame
?:
string
;
selectedTransactionType
?:
string
}
>
({
query
:
({
search
,
page
,
per_page
,
user_id
,
game_id
,
status
,
selectedGame
,
selectedTransactionType
})
=>
{
const
params
=
new
URLSearchParams
();
if
(
search
)
params
.
append
(
'search'
,
search
);
if
(
page
)
params
.
append
(
'page'
,
page
.
toString
());
...
...
@@ -69,6 +69,8 @@ export const transactionApi = createApi({
if
(
user_id
)
params
.
append
(
'user'
,
user_id
.
toString
());
if
(
game_id
)
params
.
append
(
'game'
,
game_id
.
toString
());
if
(
status
)
params
.
append
(
'status'
,
status
.
toString
());
if
(
selectedGame
)
params
.
append
(
'game'
,
selectedGame
.
toString
());
if
(
selectedTransactionType
)
params
.
append
(
'type'
,
selectedTransactionType
.
toString
());
const
queryString
=
params
.
toString
();
return
{
url
:
`/api/admin/transactions
${
queryString
?
`?
${
queryString
}
`
:
''
}
`
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment