@@ -14,6 +14,7 @@ import {
1414 ShadowsocksMethods ,
1515} from '@/service/api'
1616import { Button } from '@/components/ui/button'
17+ import { LoaderButton } from '@/components/ui/loader-button'
1718import { Input } from '@/components/ui/input'
1819import { Badge } from '@/components/ui/badge'
1920import { Card , CardContent } from '@/components/ui/card'
@@ -157,10 +158,8 @@ export default function BulkFlow({ operationType }: BulkFlowProps) {
157158 case 'expire' :
158159 return expireSeconds !== undefined
159160 case 'groups' :
160- if ( groupsOperation === 'remove' ) {
161- return selectedHasGroups . length > 0 || selectedUsers . length > 0 || selectedAdmins . length > 0
162- }
163- return selectedUsers . length > 0 || selectedAdmins . length > 0 || selectedHasGroups . length > 0
161+ // Allow proceeding even if no targets selected - will apply to all users
162+ return true
164163 default :
165164 return false
166165 }
@@ -172,10 +171,13 @@ export default function BulkFlow({ operationType }: BulkFlowProps) {
172171 }
173172
174173 const handleApply = ( ) => {
175- const totalTargets = selectedUsers . length + selectedAdmins . length + selectedGroups . length + selectedHasGroups . length
176- if ( operationType === 'groups' && groupsOperation === 'remove' && totalTargets === 0 ) {
177- toast . error ( t ( 'error' ) , { description : t ( 'bulk.noTargetsSelected' ) } )
178- return
174+ // For groups remove operation, require at least hasGroups, users, or admins to be selected
175+ if ( operationType === 'groups' && groupsOperation === 'remove' ) {
176+ const totalTargets = selectedUsers . length + selectedAdmins . length + selectedHasGroups . length
177+ if ( totalTargets === 0 ) {
178+ toast . error ( t ( 'error' ) , { description : t ( 'bulk.noTargetsSelected' ) } )
179+ return
180+ }
179181 }
180182 setShowConfirmDialog ( true )
181183 }
@@ -275,7 +277,9 @@ export default function BulkFlow({ operationType }: BulkFlowProps) {
275277 )
276278 }
277279
278- const totalTargets = selectedUsers . length + selectedAdmins . length + selectedGroups . length + ( operationType === 'groups' ? selectedHasGroups . length : 0 )
280+ // For groups operation, groups are the operation target, not user targets
281+ // So isApplyToAll should only check users, admins, and hasGroups
282+ const totalTargets = selectedUsers . length + selectedAdmins . length + ( operationType === 'groups' ? selectedHasGroups . length : selectedGroups . length )
279283 const isApplyToAll = totalTargets === 0
280284
281285 const formatTime = ( seconds : number ) => {
@@ -841,10 +845,19 @@ export default function BulkFlow({ operationType }: BulkFlowProps) {
841845 < ChevronRight className = { cn ( 'h-4 w-4' , isRTL ? 'mr-1.5 rotate-180' : 'ml-1.5' ) } />
842846 </ Button >
843847 ) : (
844- < Button onClick = { handleApply } disabled = { ! canProceedToNext ( ) } size = "sm" className = "w-full sm:w-auto" >
845- < CheckCircle className = { cn ( 'h-4 w-4' , isRTL ? 'ml-1.5' : 'mr-1.5' ) } />
846- < span > { t ( 'bulk.applyOperation' , { defaultValue : 'Apply Operation' } ) } </ span >
847- </ Button >
848+ < LoaderButton
849+ onClick = { handleApply }
850+ disabled = { ! canProceedToNext ( ) }
851+ isLoading = { proxyMutation . isPending || dataMutation . isPending || expireMutation . isPending || addGroupsMutation . isPending || removeGroupsMutation . isPending }
852+ loadingText = { t ( 'applying' , { defaultValue : 'Applying...' } ) }
853+ size = "sm"
854+ className = "w-full sm:w-auto"
855+ >
856+ < div className = "flex items-center gap-1.5" >
857+ < CheckCircle className = { cn ( 'h-4 w-4' , isRTL ? 'ml-1.5' : 'mr-1.5' ) } />
858+ < span > { t ( 'bulk.applyOperation' , { defaultValue : 'Apply Operation' } ) } </ span >
859+ </ div >
860+ </ LoaderButton >
848861 ) }
849862 </ div >
850863
0 commit comments