Compare commits
7 Commits
fix/sse-re
...
fix/bom-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
383eefce9c | ||
|
|
1676b3e1a0 | ||
|
|
06cd30e88d | ||
| f9924d35f7 | |||
| 373f3aaa79 | |||
| 66b2baf510 | |||
|
|
d26bb6da2d |
8
freecad/resources/icons/silo-rollback.svg
Normal file
8
freecad/resources/icons/silo-rollback.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#cba6f7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Counter-clockwise arrow -->
|
||||
<polyline points="1 4 1 10 7 10" stroke="#f38ba8"/>
|
||||
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10" stroke="#cba6f7"/>
|
||||
<!-- Clock hands -->
|
||||
<line x1="12" y1="7" x2="12" y2="12" stroke="#89dceb" stroke-width="1.5"/>
|
||||
<line x1="12" y1="12" x2="15" y2="14" stroke="#89dceb" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 493 B |
7
freecad/resources/icons/silo-status.svg
Normal file
7
freecad/resources/icons/silo-status.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#cba6f7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Shield shape -->
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" fill="#313244"/>
|
||||
<!-- Status bars -->
|
||||
<line x1="8" y1="10" x2="16" y2="10" stroke="#a6e3a1" stroke-width="1.5"/>
|
||||
<line x1="8" y1="14" x2="13" y2="14" stroke="#89dceb" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 435 B |
6
freecad/resources/icons/silo-tag.svg
Normal file
6
freecad/resources/icons/silo-tag.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#cba6f7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Tag shape -->
|
||||
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" fill="#313244"/>
|
||||
<!-- Tag hole -->
|
||||
<circle cx="7" cy="7" r="1.5" fill="#cba6f7" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 373 B |
@@ -1998,15 +1998,18 @@ class Silo_BOM:
|
||||
|
||||
obj = get_tracked_object(doc)
|
||||
if not obj:
|
||||
FreeCAD.Console.PrintError("No tracked Silo item in active document.\n")
|
||||
from PySide import QtGui as _qg
|
||||
|
||||
_qg.QMessageBox.warning(
|
||||
reply = QtGui.QMessageBox.question(
|
||||
None,
|
||||
"BOM",
|
||||
"This document is not registered with Silo.\nUse Silo > New to register it first.",
|
||||
"This document is not registered with Silo.\n\nRegister it now?",
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
|
||||
)
|
||||
return
|
||||
if reply != QtGui.QMessageBox.Yes:
|
||||
return
|
||||
FreeCADGui.runCommand("Silo_New")
|
||||
obj = get_tracked_object(doc)
|
||||
if not obj:
|
||||
return
|
||||
|
||||
part_number = obj.SiloPartNumber
|
||||
|
||||
@@ -2785,7 +2788,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 +2800,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 +2827,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):
|
||||
|
||||
Submodule silo-client updated: a6ac3d4d06...68a4139251
Reference in New Issue
Block a user