2727
2828class AdminDelete (BaseModal ):
2929 def __init__ (
30- self , db : AsyncSession , operation : AdminOperation , username : str , on_close : callable , * args , ** kwargs
30+ self ,
31+ db : AsyncSession ,
32+ operation : AdminOperation ,
33+ username : str ,
34+ on_close : callable ,
35+ user_count : int = 0 ,
36+ * args ,
37+ ** kwargs ,
3138 ) -> None :
3239 super ().__init__ (* args , ** kwargs )
3340 self .db = db
3441 self .operation = operation
3542 self .username = username
3643 self .on_close = on_close
44+ self .user_count = user_count
3745
3846 async def on_mount (self ) -> None :
3947 """Ensure the first button is focused."""
40- yes_button = self .query_one ( "#no" )
41- self .set_focus (yes_button )
48+ focus_target = "#delete-users" if self .user_count > 0 else "#no"
49+ self .set_focus (self . query_one ( focus_target ) )
4250
4351 def compose (self ) -> ComposeResult :
4452 with Container (classes = "modal-box-delete" ):
45- yield Static ("Are you sure about deleting this admin?" , classes = "title" )
53+ yield Static (f"Delete admin '{ self .username } '?" , classes = "title" )
54+ if self .user_count > 0 :
55+ yield Static (
56+ f"This admin has { self .user_count } users.\n You must delete them to remove the admin." ,
57+ classes = "subtitle" ,
58+ )
59+ yield Horizontal (
60+ Static ("Delete all users:" , classes = "label" ),
61+ Switch (animate = False , id = "delete-users" ),
62+ classes = "switch-container" ,
63+ )
4664 yield Horizontal (
4765 Button ("Yes" , id = "yes" , variant = "success" ),
4866 Button ("No" , id = "no" , variant = "error" ),
@@ -52,13 +70,69 @@ def compose(self) -> ComposeResult:
5270 async def on_button_pressed (self , event : Button .Pressed ) -> None :
5371 if event .button .id == "yes" :
5472 try :
73+ if self .user_count > 0 :
74+ delete_users = self .query_one ("#delete-users" ).value
75+ if delete_users :
76+ await self .operation .remove_all_users (self .db , self .username , SYSTEM_ADMIN )
77+ self .notify ("Admin users deleted successfully" , severity = "success" , title = "Success" )
5578 await self .operation .remove_admin (self .db , self .username , SYSTEM_ADMIN )
5679 self .on_close ()
5780 except ValueError as e :
5881 self .notify (str (e ), severity = "error" , title = "Error" )
5982 await self .key_escape ()
6083
6184
85+ class AdminDeleteUsers (BaseModal ):
86+ def __init__ (
87+ self ,
88+ db : AsyncSession ,
89+ operation : AdminOperation ,
90+ username : str ,
91+ on_close : callable ,
92+ user_count : int = 0 ,
93+ * args ,
94+ ** kwargs ,
95+ ) -> None :
96+ super ().__init__ (* args , ** kwargs )
97+ self .db = db
98+ self .operation = operation
99+ self .username = username
100+ self .on_close = on_close
101+ self .user_count = user_count
102+
103+ async def on_mount (self ) -> None :
104+ confirm_button = self .query_one ("#cancel" )
105+ self .set_focus (confirm_button )
106+
107+ def compose (self ) -> ComposeResult :
108+ with Container (classes = "modal-box-delete" ):
109+ yield Static (
110+ f"Delete all users belonging to admin '{ self .username } '?"
111+ f"\n Found { self .user_count } user(s). This action cannot be undone." ,
112+ classes = "title" ,
113+ )
114+ yield Horizontal (
115+ Button ("Delete Users" , id = "delete" , variant = "warning" ),
116+ Button ("Cancel" , id = "cancel" , variant = "error" ),
117+ classes = "button-container" ,
118+ )
119+
120+ async def on_button_pressed (self , event : Button .Pressed ) -> None :
121+ if event .button .id == "delete" :
122+ try :
123+ deleted = await self .operation .remove_all_users (self .db , self .username , SYSTEM_ADMIN )
124+ if deleted == 0 :
125+ self .notify ("No users were deleted (none found)" , severity = "warning" , title = "Info" )
126+ else :
127+ self .notify (
128+ f"{ deleted } users deleted for admin '{ self .username } '" , severity = "success" , title = "Success"
129+ )
130+ self .on_close ()
131+ except ValueError as e :
132+ self .notify (str (e ), severity = "error" , title = "Error" )
133+ await self .key_escape ()
134+
135+
62136class AdminResetUsage (BaseModal ):
63137 def __init__ (
64138 self , db : AsyncSession , operation : AdminOperation , username : str , on_close : callable , * args , ** kwargs
@@ -396,17 +470,15 @@ async def on_mount(self) -> None:
396470
397471 # Load existing notification preferences (notification_enable is a dict from SQLAlchemy)
398472 notif = self .admin .notification_enable or {}
399- master_on = any (
400- [
401- notif .get ("create" , False ),
402- notif .get ("modify" , False ),
403- notif .get ("delete" , False ),
404- notif .get ("status_change" , False ),
405- notif .get ("reset_data_usage" , False ),
406- notif .get ("data_reset_by_next" , False ),
407- notif .get ("subscription_revoked" , False ),
408- ]
409- )
473+ master_on = any ([
474+ notif .get ("create" , False ),
475+ notif .get ("modify" , False ),
476+ notif .get ("delete" , False ),
477+ notif .get ("status_change" , False ),
478+ notif .get ("reset_data_usage" , False ),
479+ notif .get ("data_reset_by_next" , False ),
480+ notif .get ("subscription_revoked" , False ),
481+ ])
410482
411483 self .query_one ("#notif_master" ).value = master_on
412484 self .query_one ("#notif_create" ).value = notif .get ("create" , False )
@@ -549,6 +621,7 @@ def __init__(self, *args, **kwargs) -> None:
549621 ("m" , "modify_admin" , "Modify admin" ),
550622 ("r" , "reset_admin_usage" , "Reset admin usage" ),
551623 ("d" , "delete_admin" , "Delete admin" ),
624+ ("u" , "delete_admin_users" , "Delete admin users" ),
552625 ("i" , "import_from_env" , "Import from env" ),
553626 ("p" , "previous_page" , "Previous page" ),
554627 ("n" , "next_page" , "Next page" ),
@@ -648,7 +721,20 @@ def selected_admin(self):
648721 async def action_delete_admin (self ):
649722 if not self .table .columns :
650723 return
651- self .app .push_screen (AdminDelete (self .db , self .admin_operator , self .selected_admin , self ._refresh_table ))
724+ admin = await self .admin_operator .get_validated_admin (self .db , username = self .selected_admin )
725+ user_count = len (admin .users or [])
726+ self .app .push_screen (
727+ AdminDelete (self .db , self .admin_operator , self .selected_admin , self ._refresh_table , user_count )
728+ )
729+
730+ async def action_delete_admin_users (self ):
731+ if not self .table .columns :
732+ return
733+ admin = await self .admin_operator .get_validated_admin (self .db , username = self .selected_admin )
734+ user_count = len (admin .users or [])
735+ self .app .push_screen (
736+ AdminDeleteUsers (self .db , self .admin_operator , self .selected_admin , self ._refresh_table , user_count )
737+ )
652738
653739 def _refresh_table (self ):
654740 self .run_worker (self .admins_list )
0 commit comments