""" Secret Generation Frame UI component for secret generation form. """ import customtkinter as ctk from typing import List, Dict, Callable, Optional class SecretGenerationFrame(ctk.CTkFrame): """Frame for secret generation form.""" def __init__(self, parent, on_generate: Callable = None): """ Initialize the secret generation frame. Args: parent: Parent widget on_generate: Callback function when generate button is clicked """ super().__init__(parent) self.on_generate = on_generate # Configure frame self.configure(corner_radius=10, border_width=2) # Title self.title_label = ctk.CTkLabel( self, text="Secret Generation", font=ctk.CTkFont(size=18, weight="bold") ) self.title_label.grid(row=0, column=0, columnspan=2, padx=20, pady=(20, 15), sticky="w") # Secret Description Label self.description_label = ctk.CTkLabel( self, text="Secret Description:", font=ctk.CTkFont(size=14) ) self.description_label.grid(row=1, column=0, columnspan=2, padx=20, pady=(0, 5), sticky="w") # Secret Description Entry self.description_entry = ctk.CTkEntry( self, placeholder_text="e.g., Production API Key 2025", height=40, font=ctk.CTkFont(size=14) ) self.description_entry.grid(row=2, column=0, columnspan=2, padx=20, pady=(0, 15), sticky="ew") # Key Vault Label self.vault_label = ctk.CTkLabel( self, text="Select Key Vault:", font=ctk.CTkFont(size=14) ) self.vault_label.grid(row=3, column=0, columnspan=2, padx=20, pady=(0, 5), sticky="w") # Key Vault Dropdown Button (simplified inline version) self.vault_dropdown_button = ctk.CTkButton( self, text="Please select a subscription first", command=self._open_vault_dropdown, width=600, height=40, font=ctk.CTkFont(size=14), anchor="w", state="disabled" ) self.vault_dropdown_button.grid(row=4, column=0, columnspan=2, padx=20, pady=(0, 15), sticky="ew") # Store vaults and selection self.vaults = [] self.selected_vault = None self.vault_popup = None # Remove old secrets checkbox self.remove_old_checkbox = ctk.CTkCheckBox( self, text="Remove old secrets after creating new one", font=ctk.CTkFont(size=14) ) self.remove_old_checkbox.grid(row=5, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="w") # Generate button self.generate_button = ctk.CTkButton( self, text="Generate Secret", command=self._on_generate_clicked, height=50, font=ctk.CTkFont(size=16, weight="bold"), state="disabled" ) self.generate_button.grid(row=6, column=0, columnspan=2, padx=20, pady=(0, 20), sticky="ew") # Configure grid self.grid_columnconfigure(0, weight=1) def set_vaults(self, vaults: List[Dict[str, str]]): """ Set the list of Key Vaults. Args: vaults: List of vault dictionaries with 'name' and 'resource_group' """ self.vaults = vaults if vaults: self.vault_dropdown_button.configure(state="normal") # Auto-select first vault self._select_vault(vaults[0]) else: self.vault_dropdown_button.configure(state="disabled", text="No Key Vaults found") self.selected_vault = None def _open_vault_dropdown(self): """Open the vault selection popup.""" if not self.vaults or self.vault_popup: return import tkinter as tk # Create popup self.vault_popup = tk.Toplevel(self) self.vault_popup.wm_overrideredirect(True) self.vault_popup.wm_attributes("-topmost", True) # Calculate height item_height = 44 calculated_height = len(self.vaults) * item_height + 10 popup_height = min(calculated_height, 300) # Create scrollable frame scroll_frame = ctk.CTkScrollableFrame( self.vault_popup, width=580, height=popup_height ) scroll_frame.pack(fill="both", expand=True) # Configure scroll speed to match main window (40px per scroll unit = 2x faster) scroll_frame._parent_canvas.configure(yscrollincrement=40) # Create buttons for idx, vault in enumerate(self.vaults): display_text = f"{vault['name']} (RG: {vault['resource_group']})" max_chars = 60 truncated = display_text if len(display_text) <= max_chars else display_text[:max_chars] + "..." btn = ctk.CTkButton( scroll_frame, text=truncated, command=lambda v=vault: self._select_vault_and_close(v), font=ctk.CTkFont(size=14), height=40, fg_color="transparent", border_width=1, border_color="gray50", hover_color=("gray70", "gray30"), anchor="w" ) btn.grid(row=idx, column=0, padx=5, pady=2, sticky="ew") scroll_frame.grid_columnconfigure(0, weight=1) # Bind mouse wheel def popup_scroll(event): if event.delta > 0: scroll_frame._parent_canvas.yview_scroll(-1, "units") else: scroll_frame._parent_canvas.yview_scroll(1, "units") return "break" btn.bind("", popup_scroll, add="+") # Position popup self.vault_popup.update_idletasks() x = self.vault_dropdown_button.winfo_rootx() y = self.vault_dropdown_button.winfo_rooty() + self.vault_dropdown_button.winfo_height() self.vault_popup.wm_geometry(f"+{x}+{y}") # Bind events self.vault_popup.bind("", lambda e: self._close_vault_popup()) self.vault_popup.bind("", lambda e: self._close_vault_popup()) self.vault_popup.focus_set() def _select_vault(self, vault: Dict): """Select a vault and update button text.""" self.selected_vault = vault display_text = f"{vault['name']} (RG: {vault['resource_group']})" max_chars = 60 truncated = display_text if len(display_text) <= max_chars else display_text[:max_chars] + "..." self.vault_dropdown_button.configure(text=truncated) def _select_vault_and_close(self, vault: Dict): """Select vault and close popup.""" self._select_vault(vault) self._close_vault_popup() def _close_vault_popup(self): """Close the vault popup.""" if self.vault_popup: self.vault_popup.destroy() self.vault_popup = None def _on_generate_clicked(self): """Handle generate button click.""" if self.on_generate: description = self.description_entry.get().strip() remove_old = self.remove_old_checkbox.get() vault = self.get_selected_vault() if description and vault: self.on_generate(description, vault, remove_old) def get_selected_vault(self) -> Optional[Dict[str, str]]: """ Get the currently selected Key Vault. Returns: Dict: Selected vault or None """ return self.selected_vault def get_description(self) -> str: """ Get the secret description. Returns: str: Description text """ return self.description_entry.get().strip() def get_remove_old_secrets(self) -> bool: """ Get whether to remove old secrets. Returns: bool: True if checkbox is checked """ return self.remove_old_checkbox.get() def set_enabled(self, enabled: bool): """ Enable or disable the frame. Args: enabled: Whether to enable the frame """ if enabled: self.description_entry.configure(state="normal") if self.vaults: self.vault_dropdown_button.configure(state="normal") self.remove_old_checkbox.configure(state="normal") self.generate_button.configure(state="normal") else: self.description_entry.configure(state="disabled") self.vault_dropdown_button.configure(state="disabled") self.remove_old_checkbox.configure(state="disabled") self.generate_button.configure(state="disabled") def set_generating(self, generating: bool): """ Set generating state. Args: generating: Whether currently generating """ if generating: self.generate_button.configure(state="disabled", text="Generating...") else: self.generate_button.configure(state="normal", text="Generate Secret") def set_loading_vaults(self, loading: bool): """ Set loading vaults state. Args: loading: Whether currently loading """ if loading: self.vault_dropdown_button.configure(state="disabled", text="Loading Key Vaults...") else: if self.vaults: self.vault_dropdown_button.configure(state="normal") else: self.vault_dropdown_button.configure(state="disabled", text="No Key Vaults found") def reset(self): """Reset the form to initial state.""" self.description_entry.delete(0, 'end') self.remove_old_checkbox.deselect()