@@ -8,6 +8,12 @@ import { Input } from "@/components/ui/input";
88import { Checkbox } from "@/components/ui/checkbox" ;
99import { Progress } from "@/components/ui/progress" ;
1010import { Badge } from "@/components/ui/badge" ;
11+ import {
12+ DropdownMenu ,
13+ DropdownMenuContent ,
14+ DropdownMenuItem ,
15+ DropdownMenuTrigger ,
16+ } from "@/components/ui/dropdown-menu" ;
1117import {
1218 Select ,
1319 SelectContent ,
@@ -53,6 +59,10 @@ import {
5359 Globe ,
5460 Lock ,
5561 RotateCcw ,
62+ Star ,
63+ GitFork ,
64+ ChevronDown ,
65+ Check ,
5666} from "lucide-react" ;
5767import { toast } from "sonner" ;
5868import { useTranslations } from "@/hooks/use-translations" ;
@@ -83,6 +93,7 @@ export default function AdminRepositoriesPage() {
8393 const [ deleteId , setDeleteId ] = useState < string | null > ( null ) ;
8494 const [ selectedIds , setSelectedIds ] = useState < Set < string > > ( new Set ( ) ) ;
8595 const [ syncing , setSyncing ] = useState < string | null > ( null ) ;
96+ const [ statusUpdatingId , setStatusUpdatingId ] = useState < string | null > ( null ) ;
8697 const [ batchSyncing , setBatchSyncing ] = useState ( false ) ;
8798 const [ batchDeleting , setBatchDeleting ] = useState ( false ) ;
8899 const [ showBatchDeleteConfirm , setShowBatchDeleteConfirm ] = useState ( false ) ;
@@ -147,13 +158,17 @@ export default function AdminRepositoriesPage() {
147158 }
148159 } ;
149160
150- const handleStatusChange = async ( id : string , newStatus : number ) => {
161+ const handleStatusChange = async ( id : string , newStatus : number , currentStatus ?: number ) => {
162+ if ( currentStatus === newStatus ) return ;
163+ setStatusUpdatingId ( id ) ;
151164 try {
152165 await updateRepositoryStatus ( id , newStatus ) ;
153166 toast . success ( t ( 'admin.toast.statusUpdateSuccess' ) ) ;
154167 fetchData ( ) ;
155168 } catch {
156169 toast . error ( t ( 'admin.toast.statusUpdateFailed' ) ) ;
170+ } finally {
171+ setStatusUpdatingId ( ( prev ) => ( prev === id ? null : prev ) ) ;
157172 }
158173 } ;
159174
@@ -162,7 +177,9 @@ export default function AdminRepositoriesPage() {
162177 try {
163178 const result = await syncRepositoryStats ( id ) ;
164179 if ( result . success ) {
165- toast . success ( `${ t ( 'admin.toast.syncSuccess' ) } : ⭐ ${ result . starCount } 🍴 ${ result . forkCount } ` ) ;
180+ toast . success (
181+ `${ t ( 'admin.toast.syncSuccess' ) } : ${ t ( 'admin.repositories.star' ) } ${ result . starCount } , ${ t ( 'admin.repositories.fork' ) } ${ result . forkCount } `
182+ ) ;
166183 fetchData ( ) ;
167184 } else {
168185 toast . error ( result . message || t ( 'admin.toast.syncFailed' ) ) ;
@@ -480,28 +497,57 @@ export default function AdminRepositoriesPage() {
480497 ) }
481498 </ td >
482499 < td className = "px-4 py-3" >
483- < Select
484- value = { repo . status . toString ( ) }
485- onValueChange = { ( v ) => handleStatusChange ( repo . id , parseInt ( v ) ) }
486- >
487- < SelectTrigger className = "w-[100px] transition-all duration-200" >
488- < span className = { `px-2 py-1 rounded text-xs ${ statusColors [ repo . status ] } ` } >
489- { statusLabels [ repo . status ] }
490- </ span >
491- </ SelectTrigger >
492- < SelectContent >
493- < SelectItem value = "0" > { t ( 'admin.repositories.pending' ) } </ SelectItem >
494- < SelectItem value = "1" > { t ( 'admin.repositories.processing' ) } </ SelectItem >
495- < SelectItem value = "2" > { t ( 'admin.repositories.completed' ) } </ SelectItem >
496- < SelectItem value = "3" > { t ( 'admin.repositories.failed' ) } </ SelectItem >
497- </ SelectContent >
498- </ Select >
500+ < DropdownMenu >
501+ < DropdownMenuTrigger asChild >
502+ < Button
503+ variant = "outline"
504+ size = "sm"
505+ disabled = { statusUpdatingId === repo . id }
506+ className = "h-8 w-[124px] justify-between px-2 transition-all duration-200"
507+ >
508+ < span className = { `inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs ${ statusColors [ repo . status ] } ` } >
509+ < span className = "h-1.5 w-1.5 rounded-full bg-current/80" />
510+ { statusLabels [ repo . status ] }
511+ </ span >
512+ { statusUpdatingId === repo . id ? (
513+ < Loader2 className = "h-3.5 w-3.5 animate-spin text-muted-foreground" />
514+ ) : (
515+ < ChevronDown className = "h-3.5 w-3.5 text-muted-foreground" />
516+ ) }
517+ </ Button >
518+ </ DropdownMenuTrigger >
519+ < DropdownMenuContent align = "start" className = "w-[160px]" >
520+ { [ 0 , 1 , 2 , 3 ] . map ( ( statusValue ) => (
521+ < DropdownMenuItem
522+ key = { statusValue }
523+ disabled = { repo . status === statusValue }
524+ onClick = { ( ) => handleStatusChange ( repo . id , statusValue , repo . status ) }
525+ className = "justify-between"
526+ >
527+ < span className = "inline-flex items-center gap-2" >
528+ < span className = { `h-2 w-2 rounded-full ${ statusBarColors [ statusValue ] } ` } />
529+ { statusLabels [ statusValue ] }
530+ </ span >
531+ { repo . status === statusValue ? < Check className = "h-3.5 w-3.5 text-primary" /> : null }
532+ </ DropdownMenuItem >
533+ ) ) }
534+ </ DropdownMenuContent >
535+ </ DropdownMenu >
499536 </ td >
500537 < td className = "px-4 py-3" >
501- < div className = "text-sm" >
502- < span className = "text-muted-foreground" > ⭐ { repo . starCount } </ span >
503- < span className = "ml-2 text-muted-foreground" > 🍴 { repo . forkCount } </ span >
504- < span className = "ml-2 text-muted-foreground" > 👁 { repo . viewCount } </ span >
538+ < div className = "flex items-center gap-3 text-sm text-muted-foreground" >
539+ < span className = "inline-flex items-center gap-1" >
540+ < Star className = "h-3.5 w-3.5" />
541+ { repo . starCount }
542+ </ span >
543+ < span className = "inline-flex items-center gap-1" >
544+ < GitFork className = "h-3.5 w-3.5" />
545+ { repo . forkCount }
546+ </ span >
547+ < span className = "inline-flex items-center gap-1" >
548+ < Eye className = "h-3.5 w-3.5" />
549+ { repo . viewCount }
550+ </ span >
505551 </ div >
506552 </ td >
507553 < td className = "px-4 py-3 text-sm text-muted-foreground" >
0 commit comments