04/24 Added import website in bulk
This commit is contained in:
+144
-4
@@ -375,6 +375,20 @@ class AiSummaryView(ttk.Frame):
|
||||
).pack(side="left", padx=(0, 4))
|
||||
self._criteria_tree.bind("<Double-1>",
|
||||
lambda _: self._open_edit_criterion())
|
||||
else:
|
||||
# User (read-only): provide a View Details button and double-click binding
|
||||
btn_bar = tk.Frame(self._criteria_card, bg=C["surface"], pady=4)
|
||||
btn_bar.pack(fill="x")
|
||||
tk.Button(
|
||||
btn_bar, text="👁 View Details",
|
||||
command=self._open_view_criterion,
|
||||
bg=C["surface2"], fg=C["text"],
|
||||
activebackground=C["accent"], activeforeground=C["white"],
|
||||
relief="flat", font=FONT_SMALL, cursor="hand2",
|
||||
padx=8, pady=3,
|
||||
).pack(side="left", padx=(0, 4))
|
||||
self._criteria_tree.bind("<Double-1>",
|
||||
lambda _: self._open_view_criterion())
|
||||
|
||||
tk.Label(
|
||||
self._criteria_card,
|
||||
@@ -444,6 +458,27 @@ class AiSummaryView(ttk.Frame):
|
||||
CriterionDialog(self, self.current_user,
|
||||
criterion_data=data, on_save=self._load_criteria_tree)
|
||||
|
||||
def _open_view_criterion(self):
|
||||
"""Open a read-only detail dialog for the selected criterion (user role)."""
|
||||
cid = self._get_selected_criterion_id()
|
||||
if not cid:
|
||||
show_error("Please select a criterion to view.")
|
||||
return
|
||||
try:
|
||||
from models import get_all_criteria
|
||||
all_rows = {r["id"]: r for r in get_all_criteria()}
|
||||
data = all_rows.get(cid)
|
||||
except Exception as e:
|
||||
show_error(f"Could not load criterion: {e}")
|
||||
return
|
||||
if not data:
|
||||
show_error("Criterion not found.")
|
||||
return
|
||||
logger.info(
|
||||
f"Criterion id={cid} viewed by user_id={self.current_user['id']}."
|
||||
)
|
||||
CriterionDetailDialog(self, data)
|
||||
|
||||
def _delete_criterion(self):
|
||||
cid = self._get_selected_criterion_id()
|
||||
if not cid:
|
||||
@@ -1194,6 +1229,109 @@ class AiSummaryView(ttk.Frame):
|
||||
# Criterion Add / Edit Dialog (admin only)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
class CriterionDetailDialog(tk.Toplevel):
|
||||
"""Read-only modal dialog that displays the full details of an AI evaluation criterion.
|
||||
|
||||
Shown to regular users who click 'View Details' or double-click a row in the
|
||||
criteria treeview. Admins continue to use CriterionDialog (edit mode) instead.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, criterion_data: dict):
|
||||
super().__init__(parent)
|
||||
self._data = criterion_data
|
||||
|
||||
self.title("Criterion Details")
|
||||
self.configure(bg=COLOURS["bg"])
|
||||
self.resizable(False, False)
|
||||
self.grab_set()
|
||||
self._build_ui()
|
||||
self._centre()
|
||||
|
||||
def _centre(self):
|
||||
self.update_idletasks()
|
||||
w, h = 560, 380
|
||||
x = (self.winfo_screenwidth() - w) // 2
|
||||
y = (self.winfo_screenheight() - h) // 2
|
||||
self.geometry(f"{w}x{h}+{x}+{y}")
|
||||
|
||||
def _build_ui(self):
|
||||
C = COLOURS
|
||||
d = self._data
|
||||
|
||||
# ── Header ────────────────────────────────────────────────────────────
|
||||
hdr = tk.Frame(self, bg=C["surface"], padx=24, pady=14)
|
||||
hdr.pack(fill="x")
|
||||
|
||||
status_text = "Active" if d.get("is_active") else "Inactive"
|
||||
status_color = C["accent"] if d.get("is_active") else C["danger"]
|
||||
|
||||
tk.Label(
|
||||
hdr,
|
||||
text=d.get("title") or "Untitled",
|
||||
bg=C["surface"], fg=C["text"],
|
||||
font=FONT_HEADING, wraplength=480, justify="left",
|
||||
).pack(anchor="w")
|
||||
|
||||
meta_row = tk.Frame(hdr, bg=C["surface"])
|
||||
meta_row.pack(anchor="w", pady=(4, 0))
|
||||
tk.Label(
|
||||
meta_row,
|
||||
text=f"Sort order: {d.get('sort_order', 0)}",
|
||||
bg=C["surface"], fg=C["text_dim"],
|
||||
font=FONT_SMALL,
|
||||
).pack(side="left")
|
||||
tk.Label(
|
||||
meta_row, text=" · ",
|
||||
bg=C["surface"], fg=C["text_dim"],
|
||||
font=FONT_SMALL,
|
||||
).pack(side="left")
|
||||
tk.Label(
|
||||
meta_row, text=status_text,
|
||||
bg=C["surface"], fg=status_color,
|
||||
font=FONT_SMALL,
|
||||
).pack(side="left")
|
||||
|
||||
ttk.Separator(self, orient="horizontal").pack(fill="x", padx=0)
|
||||
|
||||
# ── Description (scrollable, read-only) ───────────────────────────────
|
||||
body = tk.Frame(self, bg=C["bg"], padx=24, pady=16)
|
||||
body.pack(fill="both", expand=True)
|
||||
|
||||
tk.Label(
|
||||
body, text="Description",
|
||||
bg=C["bg"], fg=C["text_dim"],
|
||||
font=FONT_SMALL,
|
||||
).pack(anchor="w", pady=(0, 4))
|
||||
|
||||
txt_frame = tk.Frame(body, bg=C["surface2"])
|
||||
txt_frame.pack(fill="both", expand=True)
|
||||
|
||||
vsb = ttk.Scrollbar(txt_frame, orient="vertical")
|
||||
vsb.pack(side="right", fill="y")
|
||||
|
||||
txt = tk.Text(
|
||||
txt_frame, wrap="word",
|
||||
bg=C["surface2"], fg=C["text"],
|
||||
relief="flat", font=FONT,
|
||||
state="normal",
|
||||
yscrollcommand=vsb.set,
|
||||
padx=10, pady=8,
|
||||
)
|
||||
txt.insert("1.0", d.get("description") or "No description provided.")
|
||||
txt.config(state="disabled") # read-only after inserting content
|
||||
txt.pack(fill="both", expand=True)
|
||||
vsb.config(command=txt.yview)
|
||||
|
||||
# ── Footer ────────────────────────────────────────────────────────────
|
||||
ttk.Separator(self, orient="horizontal").pack(fill="x")
|
||||
footer = ttk.Frame(self)
|
||||
footer.pack(fill="x", padx=24, pady=12)
|
||||
ttk.Button(
|
||||
footer, text="Close",
|
||||
command=self.destroy,
|
||||
).pack(side="right")
|
||||
|
||||
|
||||
class CriterionDialog(tk.Toplevel):
|
||||
"""Modal dialog for creating or editing an AI evaluation criterion."""
|
||||
|
||||
@@ -1207,14 +1345,16 @@ class CriterionDialog(tk.Toplevel):
|
||||
|
||||
self.title("Edit Criterion" if self.is_edit else "Add Criterion")
|
||||
self.configure(bg=COLOURS["bg"])
|
||||
self.resizable(False, False)
|
||||
self.resizable(False, True) # allow vertical resize for varied DPI/fonts
|
||||
self.grab_set()
|
||||
self._build_ui()
|
||||
self._centre()
|
||||
|
||||
def _centre(self):
|
||||
self.update_idletasks()
|
||||
w, h = 560, 400
|
||||
# Height increased to 520 so buttons are always visible at standard DPI.
|
||||
# The dialog is vertically resizable so higher-DPI systems can expand it.
|
||||
w, h = 560, 520
|
||||
x = (self.winfo_screenwidth() - w) // 2
|
||||
y = (self.winfo_screenheight() - h) // 2
|
||||
self.geometry(f"{w}x{h}+{x}+{y}")
|
||||
@@ -1250,7 +1390,7 @@ class CriterionDialog(tk.Toplevel):
|
||||
desc_vsb = ttk.Scrollbar(desc_frame, orient="vertical")
|
||||
desc_vsb.pack(side="right", fill="y")
|
||||
self._desc_txt = tk.Text(
|
||||
desc_frame, height=6, wrap="word",
|
||||
desc_frame, height=5, wrap="word",
|
||||
bg=C["surface2"], fg=C["text"],
|
||||
insertbackground=C["text"],
|
||||
relief="flat", font=FONT,
|
||||
@@ -1545,4 +1685,4 @@ def _human_size(n: int) -> str:
|
||||
if n < 1024:
|
||||
return f"{n:.0f} {unit}"
|
||||
n /= 1024
|
||||
return f"{n:.1f} GB"
|
||||
return f"{n:.1f} GB"
|
||||
Reference in New Issue
Block a user