First commit
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
"""
|
||||
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("<MouseWheel>", 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("<Escape>", lambda e: self._close_vault_popup())
|
||||
self.vault_popup.bind("<FocusOut>", 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()
|
||||
Reference in New Issue
Block a user