@@ -18,17 +18,24 @@ import {
18
18
import { useTranslation } from "react-i18next" ;
19
19
import { useState } from "react" ;
20
20
import { InvitationForm , InvitationResponse } from "@/admin/types.ts" ;
21
- import { Button } from "@/components/ui/button.tsx" ;
22
- import { Download , Loader2 , RotateCw } from "lucide-react" ;
21
+ import { Button , TemporaryButton } from "@/components/ui/button.tsx" ;
22
+ import { Copy , Download , Loader2 , RotateCw , Trash } from "lucide-react" ;
23
23
import { useEffectAsync } from "@/utils/hook.ts" ;
24
- import { generateInvitation , getInvitationList } from "@/admin/api/chart.ts" ;
24
+ import {
25
+ deleteInvitation ,
26
+ generateInvitation ,
27
+ getInvitationList ,
28
+ } from "@/admin/api/chart.ts" ;
25
29
import { Input } from "@/components/ui/input.tsx" ;
26
30
import { useToast } from "@/components/ui/use-toast.ts" ;
27
31
import { Textarea } from "@/components/ui/textarea.tsx" ;
28
- import { saveAsFile } from "@/utils/dom.ts" ;
32
+ import { copyClipboard , saveAsFile } from "@/utils/dom.ts" ;
29
33
import { PaginationAction } from "@/components/ui/pagination.tsx" ;
34
+ import { Badge } from "@/components/ui/badge.tsx" ;
35
+ import OperationAction from "@/components/OperationAction.tsx" ;
36
+ import { toastState } from "@/api/common.ts" ;
30
37
31
- function GenerateDialog ( ) {
38
+ function GenerateDialog ( { update } : { update : ( ) => void } ) {
32
39
const { t } = useTranslation ( ) ;
33
40
const { toast } = useToast ( ) ;
34
41
const [ open , setOpen ] = useState < boolean > ( false ) ;
@@ -43,8 +50,10 @@ function GenerateDialog() {
43
50
44
51
async function generateCode ( ) {
45
52
const data = await generateInvitation ( type , Number ( quota ) , Number ( number ) ) ;
46
- if ( data . status ) setData ( data . data . join ( "\n" ) ) ;
47
- else
53
+ if ( data . status ) {
54
+ setData ( data . data . join ( "\n" ) ) ;
55
+ update ( ) ;
56
+ } else
48
57
toast ( {
49
58
title : t ( "admin.error" ) ,
50
59
description : data . message ,
@@ -167,16 +176,41 @@ function InvitationTable() {
167
176
< TableHead > { t ( "admin.type" ) } </ TableHead >
168
177
< TableHead > { t ( "admin.used" ) } </ TableHead >
169
178
< TableHead > { t ( "admin.updated-at" ) } </ TableHead >
179
+ < TableHead > { t ( "admin.action" ) } </ TableHead >
170
180
</ TableRow >
171
181
</ TableHeader >
172
182
< TableBody >
173
183
{ ( data . data || [ ] ) . map ( ( invitation , idx ) => (
174
184
< TableRow key = { idx } className = { `whitespace-nowrap` } >
175
185
< TableCell > { invitation . code } </ TableCell >
176
186
< TableCell > { invitation . quota } </ TableCell >
177
- < TableCell > { invitation . type } </ TableCell >
187
+ < TableCell >
188
+ < Badge > { invitation . type } </ Badge >
189
+ </ TableCell >
178
190
< TableCell > { t ( `admin.used-${ invitation . used } ` ) } </ TableCell >
179
191
< TableCell > { invitation . updated_at } </ TableCell >
192
+ < TableCell className = { `flex gap-2` } >
193
+ < TemporaryButton
194
+ size = { `icon` }
195
+ variant = { `outline` }
196
+ onClick = { ( ) => copyClipboard ( invitation . code ) }
197
+ >
198
+ < Copy className = { `h-4 w-4` } />
199
+ </ TemporaryButton >
200
+ < OperationAction
201
+ native
202
+ tooltip = { t ( "delete" ) }
203
+ variant = { `destructive` }
204
+ onClick = { async ( ) => {
205
+ const resp = await deleteInvitation ( invitation . code ) ;
206
+ toastState ( toast , t , resp , true ) ;
207
+
208
+ resp . status && ( await update ( ) ) ;
209
+ } }
210
+ >
211
+ < Trash className = { `h-4 w-4` } />
212
+ </ OperationAction >
213
+ </ TableCell >
180
214
</ TableRow >
181
215
) ) }
182
216
</ TableBody >
@@ -202,7 +236,7 @@ function InvitationTable() {
202
236
< Button variant = { `outline` } size = { `icon` } onClick = { update } >
203
237
< RotateCw className = { `h-4 w-4` } />
204
238
</ Button >
205
- < GenerateDialog />
239
+ < GenerateDialog update = { update } />
206
240
</ div >
207
241
</ div >
208
242
) ;
0 commit comments