Compare commits

...

5 Commits

Author SHA1 Message Date
Zoe Forbes
06cd30e88d fix: update silo-client — consistent error handling in delete_bom_entry (#59) 2026-02-08 18:29:31 -06:00
f9924d35f7 Merge pull request 'feat: enhance Database Activity pane with comments, interaction, and badges' (#11) from feature/activity-pane-enhancements into main
Reviewed-on: #11
2026-02-08 22:23:50 +00:00
373f3aaa79 Merge branch 'main' into feature/activity-pane-enhancements 2026-02-08 22:23:40 +00:00
66b2baf510 Merge pull request 'fix: SSE reconnect with exponential backoff and terminal state' (#7) from fix/sse-reconnect into main
Reviewed-on: #7
2026-02-08 22:23:23 +00:00
Zoe Forbes
d26bb6da2d feat: enhance Database Activity pane with comments, interaction, and badges
Enhance the Database Activity panel in SiloAuthDockWidget:

- Show latest revision number and comment inline per activity entry
- Truncate long descriptions with ellipsis, full text in tooltip
- Mark locally checked-out items with green color and "local" badge
- Double-click opens local file or triggers checkout from server
- Right-click context menu: Open in Create, Open in Browser,
  Copy Part Number, View Revisions

Closes #9
2026-02-08 16:23:06 -06:00
2 changed files with 105 additions and 3 deletions

View File

@@ -2785,7 +2785,7 @@ class SiloAuthDockWidget:
def _refresh_activity_panel(self):
"""Refresh the Database Activity panel if it exists."""
from PySide import QtWidgets
from PySide import QtCore, QtGui, QtWidgets
mw = FreeCADGui.getMainWindow()
if mw is None:
@@ -2797,6 +2797,24 @@ class SiloAuthDockWidget:
if activity_list is None:
return
activity_list.clear()
# Connect interaction signals (once)
if not getattr(activity_list, "_silo_connected", False):
activity_list.itemDoubleClicked.connect(self._on_activity_double_click)
activity_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
activity_list.customContextMenuRequested.connect(
lambda pos: self._on_activity_context_menu(activity_list, pos)
)
activity_list._silo_connected = True
# Collect local part numbers for badge
local_pns = set()
try:
for lf in search_local_files():
local_pns.add(lf.get("part_number", ""))
except Exception:
pass
try:
items = _client.list_items()
if isinstance(items, list):
@@ -2806,12 +2824,96 @@ class SiloAuthDockWidget:
updated = item.get("updated_at", "")
if updated:
updated = updated[:10]
activity_list.addItem(f"{pn} - {desc} - {updated}")
# Fetch latest revision info
rev_num = ""
comment = ""
try:
revs = _client.get_revisions(pn)
if revs:
latest = revs[0] if isinstance(revs, list) else revs
rev_num = str(latest.get("revision_number", ""))
comment = latest.get("comment", "") or ""
except Exception:
pass
# Truncate long descriptions
desc_display = desc
if len(desc_display) > 40:
desc_display = desc_display[:37] + "..."
# Build display text
rev_part = f" \u2013 Rev {rev_num}" if rev_num else ""
date_part = f" \u2013 {updated}" if updated else ""
local_badge = " \u25cf local" if pn in local_pns else ""
line1 = (
f"{pn} \u2013 {desc_display}{rev_part}{date_part}{local_badge}"
)
if comment:
line1 += f'\n "{comment}"'
else:
line1 += "\n (no comment)"
list_item = QtWidgets.QListWidgetItem(line1)
list_item.setData(QtCore.Qt.UserRole, pn)
if desc and len(desc) > 40:
list_item.setToolTip(desc)
if pn in local_pns:
list_item.setForeground(QtGui.QColor("#4CAF50"))
activity_list.addItem(list_item)
if activity_list.count() == 0:
activity_list.addItem("(No items in database)")
except Exception:
activity_list.addItem("(Unable to refresh activity)")
def _on_activity_double_click(self, item):
"""Open/checkout item from activity pane."""
pn = item.data(256) # Qt.UserRole
if not pn:
return
local_path = find_file_by_part_number(pn)
if local_path and local_path.exists():
FreeCAD.openDocument(str(local_path))
else:
_sync.open_item(pn)
def _on_activity_context_menu(self, activity_list, pos):
"""Show context menu for activity pane items."""
from PySide import QtCore, QtGui
item = activity_list.itemAt(pos)
if item is None:
return
pn = item.data(QtCore.Qt.UserRole)
if not pn:
return
menu = QtGui.QMenu()
open_action = menu.addAction("Open in Create")
browser_action = menu.addAction("Open in Browser")
copy_action = menu.addAction("Copy Part Number")
revisions_action = menu.addAction("View Revisions")
action = menu.exec_(activity_list.mapToGlobal(pos))
if action == open_action:
local_path = find_file_by_part_number(pn)
if local_path and local_path.exists():
FreeCAD.openDocument(str(local_path))
else:
_sync.open_item(pn)
elif action == browser_action:
import webbrowser
api_url = _get_api_url().rstrip("/")
base_url = api_url[:-4] if api_url.endswith("/api") else api_url
webbrowser.open(f"{base_url}/items/{pn}")
elif action == copy_action:
QtGui.QApplication.clipboard().setText(pn)
elif action == revisions_action:
FreeCADGui.runCommand("Silo_Info", 0)
# -- Actions ------------------------------------------------------------
def _on_login_clicked(self):