Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
12603a77
authored
Jul 04, 2026
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(redemption): add status filtering and cleanup action
parent
86021d8e
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
272 additions
and
130 deletions
+272
-130
controller/redemption.go
+2
-1
model/redemption.go
+29
-7
model/redemption_test.go
+100
-0
web/default/src/features/redemption-codes/api.ts
+7
-4
web/default/src/features/redemption-codes/components/data-table-bulk-actions.tsx
+14
-94
web/default/src/features/redemption-codes/components/redemptions-primary-buttons.tsx
+69
-8
web/default/src/features/redemption-codes/components/redemptions-table.tsx
+39
-12
web/default/src/features/redemption-codes/constants.ts
+9
-2
web/default/src/features/redemption-codes/types.ts
+1
-0
web/default/src/routes/_authenticated/redemption-codes/index.tsx
+2
-2
No files found.
controller/redemption.go
View file @
12603a77
...
...
@@ -29,8 +29,9 @@ func GetAllRedemptions(c *gin.Context) {
func
SearchRedemptions
(
c
*
gin
.
Context
)
{
keyword
:=
c
.
Query
(
"keyword"
)
status
:=
c
.
Query
(
"status"
)
pageInfo
:=
common
.
GetPageQuery
(
c
)
redemptions
,
total
,
err
:=
model
.
SearchRedemptions
(
keyword
,
pageInfo
.
GetStartIdx
(),
pageInfo
.
GetPageSize
())
redemptions
,
total
,
err
:=
model
.
SearchRedemptions
(
keyword
,
status
,
pageInfo
.
GetStartIdx
(),
pageInfo
.
GetPageSize
())
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
...
...
model/redemption.go
View file @
12603a77
...
...
@@ -60,7 +60,7 @@ func GetAllRedemptions(startIdx int, num int) (redemptions []*Redemption, total
return
redemptions
,
total
,
nil
}
func
SearchRedemptions
(
keyword
string
,
startIdx
int
,
num
int
)
(
redemptions
[]
*
Redemption
,
total
int64
,
err
error
)
{
func
SearchRedemptions
(
keyword
string
,
sta
tus
string
,
sta
rtIdx
int
,
num
int
)
(
redemptions
[]
*
Redemption
,
total
int64
,
err
error
)
{
tx
:=
DB
.
Begin
()
if
tx
.
Error
!=
nil
{
return
nil
,
0
,
tx
.
Error
...
...
@@ -71,14 +71,36 @@ func SearchRedemptions(keyword string, startIdx int, num int) (redemptions []*Re
}
}()
// Build query based on keyword type
query
:=
tx
.
Model
(
&
Redemption
{})
// Only try to convert to ID if the string represents a valid integer
if
id
,
err
:=
strconv
.
Atoi
(
keyword
);
err
==
nil
{
query
=
query
.
Where
(
"id = ? OR name LIKE ?"
,
id
,
keyword
+
"%"
)
}
else
{
query
=
query
.
Where
(
"name LIKE ?"
,
keyword
+
"%"
)
if
keyword
!=
""
{
if
id
,
err
:=
strconv
.
Atoi
(
keyword
);
err
==
nil
{
query
=
query
.
Where
(
"id = ? OR name LIKE ?"
,
id
,
keyword
+
"%"
)
}
else
{
query
=
query
.
Where
(
"name LIKE ?"
,
keyword
+
"%"
)
}
}
if
status
!=
""
{
now
:=
common
.
GetTimestamp
()
switch
status
{
case
"expired"
:
query
=
query
.
Where
(
"status = ? AND expired_time != 0 AND expired_time < ?"
,
common
.
RedemptionCodeStatusEnabled
,
now
,
)
case
strconv
.
Itoa
(
common
.
RedemptionCodeStatusEnabled
)
:
query
=
query
.
Where
(
"status = ? AND (expired_time = 0 OR expired_time >= ?)"
,
common
.
RedemptionCodeStatusEnabled
,
now
,
)
case
strconv
.
Itoa
(
common
.
RedemptionCodeStatusDisabled
)
:
query
=
query
.
Where
(
"status = ?"
,
common
.
RedemptionCodeStatusDisabled
)
case
strconv
.
Itoa
(
common
.
RedemptionCodeStatusUsed
)
:
query
=
query
.
Where
(
"status = ?"
,
common
.
RedemptionCodeStatusUsed
)
}
}
// Get total count
...
...
model/redemption_test.go
0 → 100644
View file @
12603a77
package
model
import
(
"testing"
"github.com/QuantumNous/new-api/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func
TestSearchRedemptionsFiltersAndPaginates
(
t
*
testing
.
T
)
{
require
.
NoError
(
t
,
DB
.
AutoMigrate
(
&
Redemption
{}))
require
.
NoError
(
t
,
DB
.
Session
(
&
gorm
.
Session
{
AllowGlobalUpdate
:
true
})
.
Unscoped
()
.
Delete
(
&
Redemption
{})
.
Error
)
t
.
Cleanup
(
func
()
{
require
.
NoError
(
t
,
DB
.
Session
(
&
gorm
.
Session
{
AllowGlobalUpdate
:
true
})
.
Unscoped
()
.
Delete
(
&
Redemption
{})
.
Error
)
})
now
:=
common
.
GetTimestamp
()
redemptions
:=
[]
Redemption
{
{
Id
:
1
,
Name
:
"alpha-active"
,
Key
:
"00000000000000000000000000000001"
,
Status
:
common
.
RedemptionCodeStatusEnabled
,
ExpiredTime
:
0
},
{
Id
:
2
,
Name
:
"alpha-future"
,
Key
:
"00000000000000000000000000000002"
,
Status
:
common
.
RedemptionCodeStatusEnabled
,
ExpiredTime
:
now
+
3600
},
{
Id
:
3
,
Name
:
"alpha-expired"
,
Key
:
"00000000000000000000000000000003"
,
Status
:
common
.
RedemptionCodeStatusEnabled
,
ExpiredTime
:
now
-
10
},
{
Id
:
4
,
Name
:
"beta-disabled"
,
Key
:
"00000000000000000000000000000004"
,
Status
:
common
.
RedemptionCodeStatusDisabled
,
ExpiredTime
:
0
},
{
Id
:
5
,
Name
:
"beta-used"
,
Key
:
"00000000000000000000000000000005"
,
Status
:
common
.
RedemptionCodeStatusUsed
,
ExpiredTime
:
0
},
}
require
.
NoError
(
t
,
DB
.
Create
(
&
redemptions
)
.
Error
)
tests
:=
[]
struct
{
name
string
keyword
string
status
string
startIdx
int
num
int
wantTotal
int64
wantIds
[]
int
}{
{
name
:
"no filters returns all rows"
,
num
:
10
,
wantTotal
:
5
,
wantIds
:
[]
int
{
5
,
4
,
3
,
2
,
1
},
},
{
name
:
"keyword filters by name prefix"
,
keyword
:
"alpha"
,
num
:
10
,
wantTotal
:
3
,
wantIds
:
[]
int
{
3
,
2
,
1
},
},
{
name
:
"enabled status excludes expired rows"
,
status
:
"1"
,
num
:
10
,
wantTotal
:
2
,
wantIds
:
[]
int
{
2
,
1
},
},
{
name
:
"expired status returns enabled expired rows"
,
status
:
"expired"
,
num
:
10
,
wantTotal
:
1
,
wantIds
:
[]
int
{
3
},
},
{
name
:
"disabled status"
,
status
:
"2"
,
num
:
10
,
wantTotal
:
1
,
wantIds
:
[]
int
{
4
},
},
{
name
:
"used status"
,
status
:
"3"
,
num
:
10
,
wantTotal
:
1
,
wantIds
:
[]
int
{
5
},
},
{
name
:
"pagination keeps unpaged total"
,
startIdx
:
1
,
num
:
2
,
wantTotal
:
5
,
wantIds
:
[]
int
{
4
,
3
},
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
rows
,
total
,
err
:=
SearchRedemptions
(
tt
.
keyword
,
tt
.
status
,
tt
.
startIdx
,
tt
.
num
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
wantTotal
,
total
)
gotIds
:=
make
([]
int
,
0
,
len
(
rows
))
for
_
,
row
:=
range
rows
{
gotIds
=
append
(
gotIds
,
row
.
Id
)
}
assert
.
Equal
(
t
,
tt
.
wantIds
,
gotIds
)
})
}
}
web/default/src/features/redemption-codes/api.ts
View file @
12603a77
...
...
@@ -44,10 +44,13 @@ export async function getRedemptions(
export
async
function
searchRedemptions
(
params
:
SearchRedemptionsParams
):
Promise
<
GetRedemptionsResponse
>
{
const
{
keyword
=
''
,
p
=
1
,
page_size
=
10
}
=
params
const
res
=
await
api
.
get
(
`/api/redemption/search?keyword=
${
keyword
}
&p=
${
p
}
&page_size=
${
page_size
}
`
)
const
{
keyword
=
''
,
status
=
''
,
p
=
1
,
page_size
=
10
}
=
params
const
queryParams
=
new
URLSearchParams
()
queryParams
.
set
(
'keyword'
,
keyword
)
if
(
status
)
queryParams
.
set
(
'status'
,
status
)
queryParams
.
set
(
'p'
,
String
(
p
))
queryParams
.
set
(
'page_size'
,
String
(
page_size
))
const
res
=
await
api
.
get
(
`/api/redemption/search?
${
queryParams
.
toString
()}
`
)
return
res
.
data
}
...
...
web/default/src/features/redemption-codes/components/data-table-bulk-actions.tsx
View file @
12603a77
...
...
@@ -16,25 +16,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import
{
type
Table
}
from
'@tanstack/react-table'
import
{
Trash2
}
from
'lucide-react'
import
{
useState
,
useMemo
}
from
'react'
import
type
{
Table
}
from
'@tanstack/react-table'
import
{
useMemo
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
ConfirmDialog
}
from
'@/components/confirm-dialog'
import
{
CopyButton
}
from
'@/components/copy-button'
import
{
DataTableBulkActions
as
BulkActionsToolbar
}
from
'@/components/data-table'
import
{
Button
}
from
'@/components/ui/button'
import
{
Tooltip
,
TooltipContent
,
TooltipTrigger
,
}
from
'@/components/ui/tooltip'
import
{
deleteInvalidRedemptions
}
from
'../api'
import
{
type
Redemption
}
from
'../types'
import
{
useRedemptions
}
from
'./redemptions-provider'
import
type
{
Redemption
}
from
'../types'
type
DataTableBulkActionsProps
<
TData
>
=
{
table
:
Table
<
TData
>
...
...
@@ -44,11 +33,7 @@ export function DataTableBulkActions<TData>({
table
,
}:
DataTableBulkActionsProps
<
TData
>
)
{
const
{
t
}
=
useTranslation
()
const
{
triggerRefresh
}
=
useRedemptions
()
const
[
showDeleteInvalidConfirm
,
setShowDeleteInvalidConfirm
]
=
useState
(
false
)
const
[
isDeleting
,
setIsDeleting
]
=
useState
(
false
)
const
selectedRows
=
table
.
getFilteredSelectedRowModel
().
rows
const
selectedRows
=
table
.
getSelectedRowModel
().
rows
const
contentToCopy
=
useMemo
(()
=>
{
const
selectedCodes
=
selectedRows
.
map
((
row
)
=>
{
...
...
@@ -58,82 +43,17 @@ export function DataTableBulkActions<TData>({
return
selectedCodes
.
join
(
'\n'
)
},
[
selectedRows
])
const
handleDeleteInvalid
=
async
()
=>
{
setIsDeleting
(
true
)
try
{
const
result
=
await
deleteInvalidRedemptions
()
if
(
result
.
success
)
{
const
count
=
result
.
data
||
0
toast
.
success
(
t
(
'Successfully deleted {{count}} invalid redemption codes'
,
{
count
,
})
)
table
.
resetRowSelection
()
triggerRefresh
()
setShowDeleteInvalidConfirm
(
false
)
}
}
finally
{
setIsDeleting
(
false
)
}
}
return
(
<>
<
BulkActionsToolbar
table=
{
table
}
entityName=
{
t
(
'redemption code'
)
}
>
<
CopyButton
value=
{
contentToCopy
}
variant=
'outline'
size=
'icon'
className=
'size-8'
tooltip=
{
t
(
'Copy selected codes'
)
}
successTooltip=
{
t
(
'Codes copied!'
)
}
aria
-
label=
{
t
(
'Copy selected codes'
)
}
/>
<
Tooltip
>
<
TooltipTrigger
render=
{
<
Button
variant=
'destructive'
size=
'icon'
onClick=
{
()
=>
setShowDeleteInvalidConfirm
(
true
)
}
className=
'size-8'
aria
-
label=
{
t
(
'Delete invalid redemption codes'
)
}
title=
{
t
(
'Delete invalid redemption codes'
)
}
/>
}
>
<
Trash2
/>
<
span
className=
'sr-only'
>
{
t
(
'Delete invalid codes'
)
}
</
span
>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
{
t
(
'Delete invalid codes (used/disabled/expired)'
)
}
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
BulkActionsToolbar
>
<
ConfirmDialog
destructive
open=
{
showDeleteInvalidConfirm
}
onOpenChange=
{
setShowDeleteInvalidConfirm
}
handleConfirm=
{
handleDeleteInvalid
}
isLoading=
{
isDeleting
}
className=
'max-w-md'
title=
{
t
(
'Delete Invalid Redemption Codes?'
)
}
desc=
{
<>
{
t
(
'This will delete all'
)
}
<
strong
>
{
t
(
'used'
)
}
</
strong
>
,
{
' '
}
<
strong
>
{
t
(
'disabled'
)
}
</
strong
>
{
t
(
', and'
)
}
<
strong
>
{
t
(
'expired'
)
}
</
strong
>
{
' '
}
{
t
(
'redemption codes.'
)
}
<
br
/>
{
t
(
'This action cannot be undone.'
)
}
</>
}
confirmText=
{
t
(
'Delete Invalid'
)
}
<
BulkActionsToolbar
table=
{
table
}
entityName=
{
t
(
'redemption code'
)
}
>
<
CopyButton
value=
{
contentToCopy
}
variant=
'outline'
size=
'icon'
className=
'size-8'
tooltip=
{
t
(
'Copy selected codes'
)
}
successTooltip=
{
t
(
'Codes copied!'
)
}
aria
-
label=
{
t
(
'Copy selected codes'
)
}
/>
</>
</
BulkActionsToolbar
>
)
}
web/default/src/features/redemption-codes/components/redemptions-primary-buttons.tsx
View file @
12603a77
...
...
@@ -16,22 +16,83 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import
{
Plus
}
from
'lucide-react'
import
{
Plus
,
Trash2
}
from
'lucide-react'
import
{
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
ConfirmDialog
}
from
'@/components/confirm-dialog'
import
{
Button
}
from
'@/components/ui/button'
import
{
deleteInvalidRedemptions
}
from
'../api'
import
{
ERROR_MESSAGES
}
from
'../constants'
import
{
useRedemptions
}
from
'./redemptions-provider'
export
function
RedemptionsPrimaryButtons
()
{
const
{
t
}
=
useTranslation
()
const
{
setOpen
}
=
useRedemptions
()
const
{
setOpen
,
triggerRefresh
}
=
useRedemptions
()
const
[
showDeleteInvalidConfirm
,
setShowDeleteInvalidConfirm
]
=
useState
(
false
)
const
[
isDeleting
,
setIsDeleting
]
=
useState
(
false
)
const
handleDeleteInvalid
=
async
()
=>
{
setIsDeleting
(
true
)
try
{
const
result
=
await
deleteInvalidRedemptions
()
if
(
result
.
success
)
{
const
count
=
result
.
data
||
0
toast
.
success
(
t
(
'Successfully deleted {{count}} invalid redemption codes'
,
{
count
,
})
)
triggerRefresh
()
setShowDeleteInvalidConfirm
(
false
)
}
else
{
toast
.
error
(
result
.
message
||
t
(
ERROR_MESSAGES
.
DELETE_INVALID_FAILED
))
}
}
finally
{
setIsDeleting
(
false
)
}
}
return
(
<
div
className=
'flex gap-2'
>
<
Button
size=
'sm'
onClick=
{
()
=>
setOpen
(
'create'
)
}
>
<
Plus
className=
'h-4 w-4'
/>
{
t
(
'Create Code'
)
}
</
Button
>
</
div
>
<>
<
div
className=
'flex flex-wrap gap-2'
>
<
Button
size=
'sm'
variant=
'outline'
onClick=
{
()
=>
setShowDeleteInvalidConfirm
(
true
)
}
>
<
Trash2
className=
'text-destructive h-4 w-4'
/>
{
t
(
'Delete Invalid'
)
}
</
Button
>
<
Button
size=
'sm'
onClick=
{
()
=>
setOpen
(
'create'
)
}
>
<
Plus
className=
'h-4 w-4'
/>
{
t
(
'Create Code'
)
}
</
Button
>
</
div
>
<
ConfirmDialog
destructive
open=
{
showDeleteInvalidConfirm
}
onOpenChange=
{
setShowDeleteInvalidConfirm
}
handleConfirm=
{
handleDeleteInvalid
}
isLoading=
{
isDeleting
}
className=
'max-w-md'
title=
{
t
(
'Delete Invalid Redemption Codes?'
)
}
desc=
{
<>
{
t
(
'This will delete all'
)
}
<
strong
>
{
t
(
'used'
)
}
</
strong
>
,
{
' '
}
<
strong
>
{
t
(
'disabled'
)
}
</
strong
>
{
t
(
', and'
)
}
<
strong
>
{
t
(
'expired'
)
}
</
strong
>
{
' '
}
{
t
(
'redemption codes.'
)
}
<
br
/>
{
t
(
'This action cannot be undone.'
)
}
</>
}
confirmText=
{
t
(
'Delete Invalid'
)
}
/>
</>
)
}
web/default/src/features/redemption-codes/components/redemptions-table.tsx
View file @
12603a77
...
...
@@ -20,6 +20,7 @@ import { useQuery } from '@tanstack/react-query'
import
{
getRouteApi
}
from
'@tanstack/react-router'
import
{
useMemo
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
toast
}
from
'sonner'
import
{
DISABLED_ROW_DESKTOP
,
...
...
@@ -31,7 +32,11 @@ import { useMediaQuery } from '@/hooks'
import
{
useTableUrlState
}
from
'@/hooks/use-table-url-state'
import
{
getRedemptions
,
searchRedemptions
}
from
'../api'
import
{
REDEMPTION_STATUS
,
getRedemptionStatusOptions
}
from
'../constants'
import
{
ERROR_MESSAGES
,
REDEMPTION_STATUS
,
getRedemptionStatusOptions
,
}
from
'../constants'
import
{
isRedemptionExpired
}
from
'../lib'
import
type
{
Redemption
}
from
'../types'
import
{
DataTableBulkActions
}
from
'./data-table-bulk-actions'
...
...
@@ -68,6 +73,11 @@ export function RedemptionsTable() {
globalFilter
:
{
enabled
:
true
,
key
:
'filter'
},
columnFilters
:
[{
columnId
:
'status'
,
searchKey
:
'status'
,
type
:
'array'
}],
})
const
statusFilter
=
(
columnFilters
.
find
((
filter
)
=>
filter
.
id
===
'status'
)?.
value
as
|
string
[]
|
undefined
)
??
[]
const
statusFilterValue
=
statusFilter
[
0
]
??
''
// Fetch data with React Query
const
{
data
,
isLoading
,
isFetching
}
=
useQuery
({
...
...
@@ -76,18 +86,37 @@ export function RedemptionsTable() {
pagination
.
pageIndex
+
1
,
pagination
.
pageSize
,
globalFilter
,
statusFilterValue
,
refreshTrigger
,
],
queryFn
:
async
()
=>
{
const
hasFilter
=
globalFilter
?.
trim
()
const
hasStatusFilter
=
statusFilterValue
!==
''
const
params
=
{
p
:
pagination
.
pageIndex
+
1
,
page_size
:
pagination
.
pageSize
,
}
const
result
=
hasFilter
?
await
searchRedemptions
({
...
params
,
keyword
:
globalFilter
})
:
await
getRedemptions
(
params
)
const
result
=
hasFilter
||
hasStatusFilter
?
await
searchRedemptions
({
...
params
,
keyword
:
globalFilter
,
status
:
statusFilterValue
,
})
:
await
getRedemptions
(
params
)
if
(
!
result
.
success
)
{
toast
.
error
(
result
.
message
||
t
(
hasFilter
||
hasStatusFilter
?
ERROR_MESSAGES
.
SEARCH_FAILED
:
ERROR_MESSAGES
.
LOAD_FAILED
)
)
return
{
items
:
[],
total
:
0
}
}
return
{
items
:
result
.
data
?.
items
||
[],
...
...
@@ -116,7 +145,8 @@ export function RedemptionsTable() {
onPaginationChange
,
onGlobalFilterChange
,
onColumnFiltersChange
,
manualPagination
:
!
globalFilter
,
manualPagination
:
true
,
manualFiltering
:
true
,
totalCount
:
data
?.
total
||
0
,
ensurePageInRange
,
})
...
...
@@ -149,13 +179,10 @@ export function RedemptionsTable() {
},
],
}
}
getRowClassName=
{
(
row
,
{
isMobile
})
=>
isDisabledRedemptionRow
(
row
.
original
)
?
isMobile
?
DISABLED_ROW_MOBILE
:
DISABLED_ROW_DESKTOP
:
undefined
}
getRowClassName=
{
(
row
,
{
isMobile
})
=>
{
if
(
!
isDisabledRedemptionRow
(
row
.
original
))
return
undefined
return
isMobile
?
DISABLED_ROW_MOBILE
:
DISABLED_ROW_DESKTOP
}
}
bulkActions=
{
<
DataTableBulkActions
table=
{
table
}
/>
}
/>
)
...
...
web/default/src/features/redemption-codes/constants.ts
View file @
12603a77
...
...
@@ -16,9 +16,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import
{
type
TFunction
}
from
'i18next'
import
type
{
TFunction
}
from
'i18next'
import
{
type
StatusBadgeProps
}
from
'@/components/status-badge'
import
type
{
StatusBadgeProps
}
from
'@/components/status-badge'
// ============================================================================
// Redemption Status Configuration
...
...
@@ -63,6 +63,13 @@ export const REDEMPTION_STATUSES: Record<
// Note: "Expired" is not a real DB status, it's computed from expired_time
export
const
REDEMPTION_FILTER_EXPIRED
=
'expired'
export
const
REDEMPTION_FILTER_VALUES
=
[
String
(
REDEMPTION_STATUS
.
ENABLED
),
String
(
REDEMPTION_STATUS
.
DISABLED
),
String
(
REDEMPTION_STATUS
.
USED
),
REDEMPTION_FILTER_EXPIRED
,
]
as
const
export
function
getRedemptionStatusOptions
(
t
:
TFunction
)
{
return
[
...
Object
.
values
(
REDEMPTION_STATUSES
).
map
((
config
)
=>
({
...
...
web/default/src/features/redemption-codes/types.ts
View file @
12603a77
...
...
@@ -65,6 +65,7 @@ export interface GetRedemptionsResponse {
export
interface
SearchRedemptionsParams
{
keyword
?:
string
status
?:
string
p
?:
number
page_size
?:
number
}
...
...
web/default/src/routes/_authenticated/redemption-codes/index.tsx
View file @
12603a77
...
...
@@ -20,7 +20,7 @@ import { createFileRoute, redirect } from '@tanstack/react-router'
import
z
from
'zod'
import
{
Redemptions
}
from
'@/features/redemption-codes'
import
{
REDEMPTION_
STATUS
_VALUES
}
from
'@/features/redemption-codes/constants'
import
{
REDEMPTION_
FILTER
_VALUES
}
from
'@/features/redemption-codes/constants'
import
{
ROLE
}
from
'@/lib/roles'
import
{
useAuthStore
}
from
'@/stores/auth-store'
...
...
@@ -28,7 +28,7 @@ const redemptionsSearchSchema = z.object({
page
:
z
.
number
().
optional
().
catch
(
1
),
pageSize
:
z
.
number
().
optional
().
catch
(
10
),
filter
:
z
.
string
().
optional
().
catch
(
''
),
status
:
z
.
array
(
z
.
enum
(
REDEMPTION_
STATUS
_VALUES
)).
optional
().
catch
([]),
status
:
z
.
array
(
z
.
enum
(
REDEMPTION_
FILTER
_VALUES
)).
optional
().
catch
([]),
})
export
const
Route
=
createFileRoute
(
'/_authenticated/redemption-codes/'
)({
...
...
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