04/22 user dashboard view, no credentials popup automatically
This commit is contained in:
@@ -288,18 +288,34 @@ class WebsiteDialog(tk.Toplevel):
|
||||
# Hide initially; shown when visibility='assigned'
|
||||
self._user_assign_frame.pack_forget()
|
||||
|
||||
# ── Credentials section ───────────────────────────────────────────────
|
||||
# ── Credentials section (collapsed by default) ───────────────────────
|
||||
ttk.Separator(self.inner, orient="horizontal").pack(
|
||||
fill="x", padx=24, pady=12)
|
||||
cred_header = ttk.Frame(self.inner)
|
||||
cred_header.pack(fill="x", padx=24)
|
||||
ttk.Label(cred_header, text="Login Credentials",
|
||||
style="Heading.TLabel").pack(side="left")
|
||||
ttk.Button(cred_header, text="+ Add Credential",
|
||||
command=self._add_cred_row).pack(side="right")
|
||||
|
||||
self.creds_container = ttk.Frame(self.inner)
|
||||
self.creds_container.pack(fill="x", padx=24, pady=8)
|
||||
# Toggle header row
|
||||
cred_toggle = ttk.Frame(self.inner)
|
||||
cred_toggle.pack(fill="x", padx=24)
|
||||
|
||||
ttk.Label(cred_toggle, text="Login Credentials",
|
||||
style="Heading.TLabel").pack(side="left")
|
||||
|
||||
self._cred_expanded = False
|
||||
self._cred_toggle_btn = ttk.Button(
|
||||
cred_toggle,
|
||||
text="🔑 Show Credentials",
|
||||
style="Ghost.TButton",
|
||||
command=self._toggle_credentials,
|
||||
)
|
||||
self._cred_toggle_btn.pack(side="right")
|
||||
|
||||
ttk.Button(cred_toggle, text="+ Add Credential",
|
||||
command=self._add_cred_row_visible).pack(side="right", padx=(0, 6))
|
||||
|
||||
# Collapsible body
|
||||
self._cred_body = ttk.Frame(self.inner)
|
||||
self.creds_container = ttk.Frame(self._cred_body)
|
||||
self.creds_container.pack(fill="x")
|
||||
# _cred_body is NOT packed initially — hidden by default
|
||||
|
||||
# ── Buttons ───────────────────────────────────────────────────────────
|
||||
ttk.Separator(self.inner, orient="horizontal").pack(
|
||||
@@ -325,10 +341,13 @@ class WebsiteDialog(tk.Toplevel):
|
||||
for uid, var in self._user_vars.items():
|
||||
var.set(uid in assigned_ids)
|
||||
self._on_visibility_change()
|
||||
for cred in d.get("credentials", []):
|
||||
existing_creds = d.get("credentials", [])
|
||||
for cred in existing_creds:
|
||||
self._add_cred_row(cred)
|
||||
else:
|
||||
self._add_cred_row()
|
||||
# Auto-expand credentials section if creds already exist
|
||||
if existing_creds:
|
||||
self._expand_credentials()
|
||||
# For new websites: credentials section stays collapsed
|
||||
|
||||
def _on_visibility_change(self):
|
||||
"""Show or hide the user assignment panel based on visibility selection."""
|
||||
@@ -338,6 +357,27 @@ class WebsiteDialog(tk.Toplevel):
|
||||
else:
|
||||
self._user_assign_frame.pack_forget()
|
||||
|
||||
def _expand_credentials(self):
|
||||
"""Show the credentials body and update the toggle button label."""
|
||||
self._cred_expanded = True
|
||||
self._cred_body.pack(fill="x", padx=24, pady=(4, 0))
|
||||
self._cred_toggle_btn.config(text="🔒 Hide Credentials")
|
||||
|
||||
def _toggle_credentials(self):
|
||||
"""Show or hide the collapsible credentials section."""
|
||||
if self._cred_expanded:
|
||||
self._cred_expanded = False
|
||||
self._cred_body.pack_forget()
|
||||
self._cred_toggle_btn.config(text="🔑 Show Credentials")
|
||||
else:
|
||||
self._expand_credentials()
|
||||
|
||||
def _add_cred_row_visible(self):
|
||||
"""Expand the section (if collapsed) then add an empty credential row."""
|
||||
if not self._cred_expanded:
|
||||
self._expand_credentials()
|
||||
self._add_cred_row()
|
||||
|
||||
def _add_cred_row(self, cred=None):
|
||||
frame = ttk.Frame(self.creds_container, style="Surface.TFrame")
|
||||
frame.pack(fill="x", pady=4, ipady=4)
|
||||
|
||||
@@ -156,11 +156,12 @@ class AiSummaryView(ttk.Frame):
|
||||
try:
|
||||
import configparser
|
||||
from config import CONFIG_FILE
|
||||
from utils.config_crypto import decrypt_value
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(CONFIG_FILE, encoding="utf-8")
|
||||
if cfg.has_section(_CFG_SECTION):
|
||||
self._api_key_var.set(
|
||||
cfg.get(_CFG_SECTION, _CFG_KEY_KEY, fallback=""))
|
||||
raw_key = cfg.get(_CFG_SECTION, _CFG_KEY_KEY, fallback="")
|
||||
self._api_key_var.set(decrypt_value(raw_key))
|
||||
model = cfg.get(_CFG_SECTION, _CFG_KEY_MODEL,
|
||||
fallback=GROQ_MODELS[0])
|
||||
if model in GROQ_MODELS:
|
||||
@@ -169,15 +170,17 @@ class AiSummaryView(ttk.Frame):
|
||||
logger.warning(f"Could not load Groq config: {e}")
|
||||
|
||||
def _save_config(self):
|
||||
"""Persist API key / model to config.ini [groq] section."""
|
||||
"""Persist API key / model to config.ini [groq] section (key encrypted)."""
|
||||
try:
|
||||
import configparser
|
||||
from config import CONFIG_FILE
|
||||
from utils.config_crypto import encrypt_value
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(CONFIG_FILE, encoding="utf-8")
|
||||
if not cfg.has_section(_CFG_SECTION):
|
||||
cfg.add_section(_CFG_SECTION)
|
||||
cfg.set(_CFG_SECTION, _CFG_KEY_KEY, self._api_key_var.get().strip())
|
||||
cfg.set(_CFG_SECTION, _CFG_KEY_KEY,
|
||||
encrypt_value(self._api_key_var.get().strip()))
|
||||
cfg.set(_CFG_SECTION, _CFG_KEY_MODEL, self._model_var.get())
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as fh:
|
||||
cfg.write(fh)
|
||||
|
||||
@@ -362,7 +362,24 @@ class UserDashboardView(ttk.Frame):
|
||||
activeforeground=COLOURS["accent"],
|
||||
relief="flat", font=FONT_SMALL, cursor="hand2",
|
||||
padx=10, pady=4,
|
||||
).pack()
|
||||
).pack(pady=(0, 6))
|
||||
|
||||
# 🔑 Credentials button — only shown if this site has saved credentials
|
||||
try:
|
||||
from models import get_website_credentials
|
||||
creds = get_website_credentials(wid)
|
||||
except Exception:
|
||||
creds = []
|
||||
if creds:
|
||||
tk.Button(
|
||||
btn_frame, text="🔑 Credentials",
|
||||
command=lambda s=site, c=creds: CredentialsPopup(self, s["name"], c),
|
||||
bg=COLOURS["surface2"], fg=COLOURS["text"],
|
||||
activebackground=COLOURS["accent"],
|
||||
activeforeground=COLOURS["white"],
|
||||
relief="flat", font=FONT_SMALL, cursor="hand2",
|
||||
padx=10, pady=4,
|
||||
).pack()
|
||||
|
||||
# ─── Bulk Actions ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -507,11 +524,6 @@ class UserDashboardView(ttk.Frame):
|
||||
except Exception as e:
|
||||
show_error(f"Could not open URL:\n{e}")
|
||||
|
||||
from models import get_website_credentials
|
||||
creds = get_website_credentials(site["id"])
|
||||
if creds:
|
||||
CredentialsPopup(self, site["name"], creds)
|
||||
|
||||
def _mark_checked(self, site: dict):
|
||||
try:
|
||||
from models import mark_website_checked
|
||||
@@ -647,22 +659,39 @@ class CredentialsPopup(tk.Toplevel):
|
||||
label = cred.get("label") or "Default"
|
||||
tk.Label(frame, text=label, font=FONT_BOLD,
|
||||
bg=COLOURS["surface"], fg=COLOURS["accent"]).grid(
|
||||
row=0, column=0, columnspan=4, sticky="w", padx=10, pady=(4, 2))
|
||||
row=0, column=0, columnspan=6, sticky="w", padx=10, pady=(4, 2))
|
||||
|
||||
# ── Username row ──────────────────────────────────────────────────
|
||||
tk.Label(frame, text="Username:", bg=COLOURS["surface"],
|
||||
fg=COLOURS["text_dim"]).grid(
|
||||
row=1, column=0, sticky="w", padx=(10, 4))
|
||||
tk.Label(frame, text=cred["username"], bg=COLOURS["surface"],
|
||||
fg=COLOURS["text"], font=FONT_BOLD).grid(
|
||||
row=1, column=1, sticky="w", padx=(0, 20))
|
||||
row=1, column=1, sticky="w", padx=(0, 8))
|
||||
|
||||
user_copy_btn = tk.Button(
|
||||
frame, text="📋", width=3,
|
||||
bg=COLOURS["surface2"], fg=COLOURS["text_dim"],
|
||||
activebackground=COLOURS["accent"], activeforeground=COLOURS["white"],
|
||||
relief="flat", cursor="hand2", font=FONT_SMALL,
|
||||
)
|
||||
user_copy_btn.grid(row=1, column=2, padx=(0, 16))
|
||||
|
||||
def _copy_user(c=cred, b=user_copy_btn):
|
||||
self.clipboard_clear()
|
||||
self.clipboard_append(c["username"])
|
||||
b.config(text="✔", fg=COLOURS["success"])
|
||||
self.after(2000, lambda: b.config(text="📋", fg=COLOURS["text_dim"]))
|
||||
user_copy_btn.config(command=_copy_user)
|
||||
|
||||
# ── Password row ──────────────────────────────────────────────────
|
||||
tk.Label(frame, text="Password:", bg=COLOURS["surface"],
|
||||
fg=COLOURS["text_dim"]).grid(
|
||||
row=1, column=2, sticky="w", padx=(0, 4))
|
||||
row=1, column=3, sticky="w", padx=(0, 4))
|
||||
pw_var = tk.StringVar(value="••••••••")
|
||||
tk.Label(frame, textvariable=pw_var, bg=COLOURS["surface"],
|
||||
fg=COLOURS["text"], font=FONT_BOLD).grid(
|
||||
row=1, column=3, sticky="w")
|
||||
row=1, column=4, sticky="w", padx=(0, 4))
|
||||
|
||||
revealed = [False]
|
||||
def toggle(c=cred, v=pw_var, r=revealed):
|
||||
@@ -671,7 +700,24 @@ class CredentialsPopup(tk.Toplevel):
|
||||
tk.Button(frame, text="👁", command=toggle,
|
||||
bg=COLOURS["surface"], fg=COLOURS["text_dim"],
|
||||
relief="flat", cursor="hand2").grid(
|
||||
row=1, column=4, padx=(8, 10))
|
||||
row=1, column=5, padx=(0, 4))
|
||||
|
||||
pw_copy_btn = tk.Button(
|
||||
frame, text="📋", width=3,
|
||||
bg=COLOURS["surface2"], fg=COLOURS["text_dim"],
|
||||
activebackground=COLOURS["accent"], activeforeground=COLOURS["white"],
|
||||
relief="flat", cursor="hand2", font=FONT_SMALL,
|
||||
)
|
||||
pw_copy_btn.grid(row=1, column=6, padx=(0, 10))
|
||||
|
||||
def _copy_pw(c=cred, b=pw_copy_btn):
|
||||
self.clipboard_clear()
|
||||
self.clipboard_append(c["password"])
|
||||
b.config(text="✔", fg=COLOURS["success"])
|
||||
# Auto-clear clipboard after 15 s for security
|
||||
self.after(15_000, self.clipboard_clear)
|
||||
self.after(2000, lambda: b.config(text="📋", fg=COLOURS["text_dim"]))
|
||||
pw_copy_btn.config(command=_copy_pw)
|
||||
|
||||
ttk.Button(self, text="Close", command=self.destroy).pack(pady=16)
|
||||
self._centre()
|
||||
|
||||
Reference in New Issue
Block a user