Add status polling and CLI status command for tool moderation
- Add QTimer polling every 30s when tools are pending/changes_requested - Display changes_requested (⚠) and rejected (✗) states in tool tree - Show moderator feedback in tool info panel - Add `cmdforge registry status <tool>` CLI command with --sync flag - Update _sync_tool to persist registry_feedback field Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cd4c0682e8
commit
64b9e8e70d
|
|
@ -186,6 +186,12 @@ def main():
|
||||||
p_reg_mytools = registry_sub.add_parser("my-tools", help="List your published tools")
|
p_reg_mytools = registry_sub.add_parser("my-tools", help="List your published tools")
|
||||||
p_reg_mytools.set_defaults(func=cmd_registry)
|
p_reg_mytools.set_defaults(func=cmd_registry)
|
||||||
|
|
||||||
|
# registry status
|
||||||
|
p_reg_status = registry_sub.add_parser("status", help="Check moderation status of a tool")
|
||||||
|
p_reg_status.add_argument("tool", help="Tool name to check status for")
|
||||||
|
p_reg_status.add_argument("--sync", action="store_true", help="Sync status from server and update local config")
|
||||||
|
p_reg_status.set_defaults(func=cmd_registry)
|
||||||
|
|
||||||
# registry browse
|
# registry browse
|
||||||
p_reg_browse = registry_sub.add_parser("browse", help="Browse tools (TUI)")
|
p_reg_browse = registry_sub.add_parser("browse", help="Browse tools (TUI)")
|
||||||
p_reg_browse.set_defaults(func=cmd_registry)
|
p_reg_browse.set_defaults(func=cmd_registry)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ def cmd_registry(args):
|
||||||
return _cmd_registry_publish(args)
|
return _cmd_registry_publish(args)
|
||||||
elif args.registry_cmd == "my-tools":
|
elif args.registry_cmd == "my-tools":
|
||||||
return _cmd_registry_my_tools(args)
|
return _cmd_registry_my_tools(args)
|
||||||
|
elif args.registry_cmd == "status":
|
||||||
|
return _cmd_registry_status(args)
|
||||||
elif args.registry_cmd == "browse":
|
elif args.registry_cmd == "browse":
|
||||||
return _cmd_registry_browse(args)
|
return _cmd_registry_browse(args)
|
||||||
elif args.registry_cmd == "config":
|
elif args.registry_cmd == "config":
|
||||||
|
|
@ -45,7 +47,8 @@ def cmd_registry(args):
|
||||||
print(" update Update local index cache")
|
print(" update Update local index cache")
|
||||||
print(" publish [path] Publish a tool")
|
print(" publish [path] Publish a tool")
|
||||||
print(" my-tools List your published tools")
|
print(" my-tools List your published tools")
|
||||||
print(" browse Browse tools (TUI)")
|
print(" status <tool> Check moderation status of a tool")
|
||||||
|
print(" browse Browse tools (GUI)")
|
||||||
print(" config [action] Manage registry settings (admin)")
|
print(" config [action] Manage registry settings (admin)")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
@ -503,6 +506,118 @@ def _cmd_registry_my_tools(args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_registry_status(args):
|
||||||
|
"""Check moderation status of a tool."""
|
||||||
|
from ..registry_client import RegistryError, get_client
|
||||||
|
from ..tool import get_tools_dir
|
||||||
|
|
||||||
|
tool_name = args.tool
|
||||||
|
do_sync = getattr(args, 'sync', False)
|
||||||
|
|
||||||
|
# Check if tool exists locally
|
||||||
|
config_path = get_tools_dir() / tool_name / "config.yaml"
|
||||||
|
if not config_path.exists():
|
||||||
|
print(f"Error: Tool '{tool_name}' not found locally", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
config_data = yaml.safe_load(config_path.read_text()) or {}
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
print(f"Error reading tool config: {e}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
local_status = config_data.get("registry_status", "not_published")
|
||||||
|
local_hash = config_data.get("registry_hash")
|
||||||
|
local_feedback = config_data.get("registry_feedback")
|
||||||
|
|
||||||
|
if not local_hash:
|
||||||
|
print(f"Tool '{tool_name}' has not been published to the registry.")
|
||||||
|
print()
|
||||||
|
print("Publish with: cmdforge registry publish")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# If syncing, fetch from server
|
||||||
|
if do_sync:
|
||||||
|
try:
|
||||||
|
client = get_client()
|
||||||
|
status_data = client.get_my_tool_status(tool_name)
|
||||||
|
|
||||||
|
new_status = status_data.get("status", "pending")
|
||||||
|
new_hash = status_data.get("config_hash")
|
||||||
|
new_feedback = status_data.get("feedback")
|
||||||
|
|
||||||
|
changed = False
|
||||||
|
if local_status != new_status:
|
||||||
|
config_data["registry_status"] = new_status
|
||||||
|
local_status = new_status
|
||||||
|
changed = True
|
||||||
|
if new_hash and local_hash != new_hash:
|
||||||
|
config_data["registry_hash"] = new_hash
|
||||||
|
local_hash = new_hash
|
||||||
|
changed = True
|
||||||
|
if new_feedback != local_feedback:
|
||||||
|
if new_feedback:
|
||||||
|
config_data["registry_feedback"] = new_feedback
|
||||||
|
local_feedback = new_feedback
|
||||||
|
elif "registry_feedback" in config_data:
|
||||||
|
del config_data["registry_feedback"]
|
||||||
|
local_feedback = None
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
|
||||||
|
print("Status synced from server.\n")
|
||||||
|
|
||||||
|
except RegistryError as e:
|
||||||
|
if e.code == "UNAUTHORIZED":
|
||||||
|
print("Not logged in. Set your registry token to sync.", file=sys.stderr)
|
||||||
|
elif e.code == "TOOL_NOT_FOUND":
|
||||||
|
print(f"Tool '{tool_name}' not found in registry.", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(f"Error syncing: {e.message}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error syncing: {e}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Display status
|
||||||
|
print(f"Tool: {tool_name}")
|
||||||
|
print(f"Registry Hash: {local_hash[:16]}...")
|
||||||
|
|
||||||
|
status_colors = {
|
||||||
|
"approved": "\033[32mApproved\033[0m", # Green
|
||||||
|
"pending": "\033[33mPending Review\033[0m", # Yellow
|
||||||
|
"changes_requested": "\033[93mChanges Requested\033[0m", # Light yellow/orange
|
||||||
|
"rejected": "\033[31mRejected\033[0m", # Red
|
||||||
|
}
|
||||||
|
status_display = status_colors.get(local_status, local_status)
|
||||||
|
print(f"Status: {status_display}")
|
||||||
|
|
||||||
|
if local_feedback:
|
||||||
|
print()
|
||||||
|
print("Feedback from moderator:")
|
||||||
|
print("-" * 40)
|
||||||
|
print(local_feedback)
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
if local_status == "changes_requested":
|
||||||
|
print()
|
||||||
|
print("Action required: Address the feedback above and republish.")
|
||||||
|
print(" cmdforge registry publish")
|
||||||
|
elif local_status == "rejected":
|
||||||
|
print()
|
||||||
|
print("Your tool was rejected. Review the feedback above.")
|
||||||
|
elif local_status == "pending":
|
||||||
|
print()
|
||||||
|
print("Your tool is waiting for moderator review.")
|
||||||
|
print("Use --sync to check for updates.")
|
||||||
|
elif local_status == "approved":
|
||||||
|
print()
|
||||||
|
print("Your tool is live in the registry!")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _cmd_registry_browse(args):
|
def _cmd_registry_browse(args):
|
||||||
"""Browse tools (GUI)."""
|
"""Browse tools (GUI)."""
|
||||||
from ..gui import run_gui
|
from ..gui import run_gui
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from PySide6.QtWidgets import (
|
||||||
QTreeWidget, QTreeWidgetItem, QTextEdit, QLabel,
|
QTreeWidget, QTreeWidgetItem, QTextEdit, QLabel,
|
||||||
QPushButton, QGroupBox, QMessageBox, QFrame
|
QPushButton, QGroupBox, QMessageBox, QFrame
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt, QThread, Signal
|
from PySide6.QtCore import Qt, QThread, Signal, QTimer
|
||||||
from PySide6.QtGui import QFont, QColor, QBrush
|
from PySide6.QtGui import QFont, QColor, QBrush
|
||||||
|
|
||||||
from ...tool import (
|
from ...tool import (
|
||||||
|
|
@ -68,9 +68,11 @@ class StatusSyncWorker(QThread):
|
||||||
status_data = client.get_my_tool_status(tool_name)
|
status_data = client.get_my_tool_status(tool_name)
|
||||||
new_status = status_data.get("status", "pending")
|
new_status = status_data.get("status", "pending")
|
||||||
new_hash = status_data.get("config_hash")
|
new_hash = status_data.get("config_hash")
|
||||||
|
new_feedback = status_data.get("feedback")
|
||||||
|
|
||||||
old_status = config_data.get("registry_status", "pending")
|
old_status = config_data.get("registry_status", "pending")
|
||||||
old_hash = config_data.get("registry_hash")
|
old_hash = config_data.get("registry_hash")
|
||||||
|
old_feedback = config_data.get("registry_feedback")
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if old_status != new_status:
|
if old_status != new_status:
|
||||||
|
|
@ -79,6 +81,12 @@ class StatusSyncWorker(QThread):
|
||||||
if new_hash and old_hash != new_hash:
|
if new_hash and old_hash != new_hash:
|
||||||
config_data["registry_hash"] = new_hash
|
config_data["registry_hash"] = new_hash
|
||||||
changed = True
|
changed = True
|
||||||
|
if new_feedback != old_feedback:
|
||||||
|
if new_feedback:
|
||||||
|
config_data["registry_feedback"] = new_feedback
|
||||||
|
elif "registry_feedback" in config_data:
|
||||||
|
del config_data["registry_feedback"]
|
||||||
|
changed = True
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
|
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
|
||||||
|
|
@ -93,6 +101,8 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]:
|
||||||
Tuple of (state, registry_hash) where state is:
|
Tuple of (state, registry_hash) where state is:
|
||||||
- "published" - approved in registry and current hash matches
|
- "published" - approved in registry and current hash matches
|
||||||
- "pending" - submitted but awaiting moderation
|
- "pending" - submitted but awaiting moderation
|
||||||
|
- "changes_requested" - admin requested changes before approval
|
||||||
|
- "rejected" - rejected by admin
|
||||||
- "modified" - has registry_hash but current hash differs
|
- "modified" - has registry_hash but current hash differs
|
||||||
- "local" - no registry_hash (never published/downloaded)
|
- "local" - no registry_hash (never published/downloaded)
|
||||||
"""
|
"""
|
||||||
|
|
@ -123,8 +133,12 @@ def get_tool_publish_state(tool_name: str) -> Tuple[str, Optional[str]]:
|
||||||
# Hash matches - check moderation status
|
# Hash matches - check moderation status
|
||||||
if registry_status == "approved":
|
if registry_status == "approved":
|
||||||
return ("published", registry_hash)
|
return ("published", registry_hash)
|
||||||
|
elif registry_status == "changes_requested":
|
||||||
|
return ("changes_requested", registry_hash)
|
||||||
|
elif registry_status == "rejected":
|
||||||
|
return ("rejected", registry_hash)
|
||||||
else:
|
else:
|
||||||
# pending, rejected, or unknown
|
# pending or unknown
|
||||||
return ("pending", registry_hash)
|
return ("pending", registry_hash)
|
||||||
except Exception:
|
except Exception:
|
||||||
return ("local", None)
|
return ("local", None)
|
||||||
|
|
@ -139,6 +153,8 @@ class ToolsPage(QWidget):
|
||||||
self._current_tool = None
|
self._current_tool = None
|
||||||
self._sync_worker = None
|
self._sync_worker = None
|
||||||
self._syncing = False # Prevent re-sync during update
|
self._syncing = False # Prevent re-sync during update
|
||||||
|
self._poll_timer = None # Timer for automatic status polling
|
||||||
|
self._has_pending_tools = False # Track if we need to poll
|
||||||
self._setup_ui()
|
self._setup_ui()
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
|
@ -269,6 +285,7 @@ class ToolsPage(QWidget):
|
||||||
self.tool_tree.clear()
|
self.tool_tree.clear()
|
||||||
self._current_tool = None
|
self._current_tool = None
|
||||||
self.info_text.clear()
|
self.info_text.clear()
|
||||||
|
self._has_pending_tools = False # Reset, will be set during tree building
|
||||||
|
|
||||||
tools = list_tools()
|
tools = list_tools()
|
||||||
|
|
||||||
|
|
@ -301,6 +318,10 @@ class ToolsPage(QWidget):
|
||||||
# Get publish state
|
# Get publish state
|
||||||
state, registry_hash = get_tool_publish_state(name)
|
state, registry_hash = get_tool_publish_state(name)
|
||||||
|
|
||||||
|
# Track if we have pending tools for polling
|
||||||
|
if state in ("pending", "changes_requested"):
|
||||||
|
self._has_pending_tools = True
|
||||||
|
|
||||||
# Show state indicator in display name
|
# Show state indicator in display name
|
||||||
if state == "published":
|
if state == "published":
|
||||||
display_name = f"{name} ✓"
|
display_name = f"{name} ✓"
|
||||||
|
|
@ -310,6 +331,14 @@ class ToolsPage(QWidget):
|
||||||
display_name = f"{name} ◐"
|
display_name = f"{name} ◐"
|
||||||
tooltip = "Submitted to registry - pending review"
|
tooltip = "Submitted to registry - pending review"
|
||||||
color = QColor(214, 158, 46) # Yellow/amber
|
color = QColor(214, 158, 46) # Yellow/amber
|
||||||
|
elif state == "changes_requested":
|
||||||
|
display_name = f"{name} ⚠"
|
||||||
|
tooltip = "Changes requested - see feedback"
|
||||||
|
color = QColor(245, 158, 11) # Orange/amber
|
||||||
|
elif state == "rejected":
|
||||||
|
display_name = f"{name} ✗"
|
||||||
|
tooltip = "Rejected by moderator"
|
||||||
|
color = QColor(220, 38, 38) # Red
|
||||||
elif state == "modified":
|
elif state == "modified":
|
||||||
display_name = f"{name} ●"
|
display_name = f"{name} ●"
|
||||||
tooltip = "Published to registry - local modifications"
|
tooltip = "Published to registry - local modifications"
|
||||||
|
|
@ -346,6 +375,9 @@ class ToolsPage(QWidget):
|
||||||
# Start background sync for published tools
|
# Start background sync for published tools
|
||||||
self._start_background_sync(tools)
|
self._start_background_sync(tools)
|
||||||
|
|
||||||
|
# Manage polling timer based on pending state
|
||||||
|
self._manage_poll_timer()
|
||||||
|
|
||||||
def _start_background_sync(self, tools: list):
|
def _start_background_sync(self, tools: list):
|
||||||
"""Start background sync for tools that have been published."""
|
"""Start background sync for tools that have been published."""
|
||||||
if self._syncing:
|
if self._syncing:
|
||||||
|
|
@ -385,6 +417,51 @@ class ToolsPage(QWidget):
|
||||||
"""Handle background sync completion."""
|
"""Handle background sync completion."""
|
||||||
self._syncing = False
|
self._syncing = False
|
||||||
|
|
||||||
|
def _manage_poll_timer(self):
|
||||||
|
"""Start or stop the polling timer based on pending tools."""
|
||||||
|
config = load_config()
|
||||||
|
should_poll = self._has_pending_tools and config.registry.token
|
||||||
|
|
||||||
|
if should_poll:
|
||||||
|
if not self._poll_timer:
|
||||||
|
# Create timer that polls every 30 seconds
|
||||||
|
self._poll_timer = QTimer(self)
|
||||||
|
self._poll_timer.timeout.connect(self._poll_status)
|
||||||
|
if not self._poll_timer.isActive():
|
||||||
|
self._poll_timer.start(30000) # 30 seconds
|
||||||
|
else:
|
||||||
|
# No pending tools, stop polling
|
||||||
|
if self._poll_timer and self._poll_timer.isActive():
|
||||||
|
self._poll_timer.stop()
|
||||||
|
|
||||||
|
def _poll_status(self):
|
||||||
|
"""Timer callback to poll status for pending tools."""
|
||||||
|
if self._syncing:
|
||||||
|
return # Already syncing
|
||||||
|
|
||||||
|
# Get list of tools with pending status
|
||||||
|
tools = list_tools()
|
||||||
|
pending_tools = []
|
||||||
|
|
||||||
|
for name in tools:
|
||||||
|
config_path = get_tools_dir() / name / "config.yaml"
|
||||||
|
if config_path.exists():
|
||||||
|
try:
|
||||||
|
config_data = yaml.safe_load(config_path.read_text()) or {}
|
||||||
|
status = config_data.get("registry_status")
|
||||||
|
if status in ("pending", "changes_requested") and config_data.get("registry_hash"):
|
||||||
|
pending_tools.append(name)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if pending_tools:
|
||||||
|
self._start_background_sync(pending_tools)
|
||||||
|
else:
|
||||||
|
# No more pending tools, stop timer
|
||||||
|
if self._poll_timer and self._poll_timer.isActive():
|
||||||
|
self._poll_timer.stop()
|
||||||
|
self._has_pending_tools = False
|
||||||
|
|
||||||
def _on_tool_status_updated(self, tool_name: str):
|
def _on_tool_status_updated(self, tool_name: str):
|
||||||
"""Handle background sync updating a tool's status."""
|
"""Handle background sync updating a tool's status."""
|
||||||
# Refresh the display - _syncing flag prevents re-triggering sync
|
# Refresh the display - _syncing flag prevents re-triggering sync
|
||||||
|
|
@ -420,6 +497,17 @@ class ToolsPage(QWidget):
|
||||||
if tool_name:
|
if tool_name:
|
||||||
self._edit_tool()
|
self._edit_tool()
|
||||||
|
|
||||||
|
def _get_tool_feedback(self, tool_name: str) -> Optional[str]:
|
||||||
|
"""Get the feedback for a tool from its config."""
|
||||||
|
config_path = get_tools_dir() / tool_name / "config.yaml"
|
||||||
|
if config_path.exists():
|
||||||
|
try:
|
||||||
|
config_data = yaml.safe_load(config_path.read_text()) or {}
|
||||||
|
return config_data.get("registry_feedback")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
def _show_tool_info(self, tool: Tool):
|
def _show_tool_info(self, tool: Tool):
|
||||||
"""Display tool information."""
|
"""Display tool information."""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
@ -443,6 +531,36 @@ class ToolsPage(QWidget):
|
||||||
"border-radius: 4px; margin-bottom: 12px; font-size: 12px;'>"
|
"border-radius: 4px; margin-bottom: 12px; font-size: 12px;'>"
|
||||||
"◐ Submitted to registry - pending review</p>"
|
"◐ Submitted to registry - pending review</p>"
|
||||||
)
|
)
|
||||||
|
elif state == "changes_requested":
|
||||||
|
lines.append(
|
||||||
|
"<p style='background: #fef3c7; color: #92400e; padding: 6px 10px; "
|
||||||
|
"border-radius: 4px; margin-bottom: 8px; font-size: 12px;'>"
|
||||||
|
"⚠ Changes requested - please address feedback and republish</p>"
|
||||||
|
)
|
||||||
|
# Show feedback if available
|
||||||
|
feedback = self._get_tool_feedback(tool.name)
|
||||||
|
if feedback:
|
||||||
|
feedback_escaped = feedback.replace("<", "<").replace(">", ">").replace("\n", "<br>")
|
||||||
|
lines.append(
|
||||||
|
f"<div style='background: #fff7ed; border-left: 3px solid #f59e0b; "
|
||||||
|
f"padding: 8px 12px; margin-bottom: 12px; font-size: 12px;'>"
|
||||||
|
f"<strong>Feedback:</strong><br>{feedback_escaped}</div>"
|
||||||
|
)
|
||||||
|
elif state == "rejected":
|
||||||
|
lines.append(
|
||||||
|
"<p style='background: #fee2e2; color: #991b1b; padding: 6px 10px; "
|
||||||
|
"border-radius: 4px; margin-bottom: 8px; font-size: 12px;'>"
|
||||||
|
"✗ Rejected by moderator</p>"
|
||||||
|
)
|
||||||
|
# Show feedback if available
|
||||||
|
feedback = self._get_tool_feedback(tool.name)
|
||||||
|
if feedback:
|
||||||
|
feedback_escaped = feedback.replace("<", "<").replace(">", ">").replace("\n", "<br>")
|
||||||
|
lines.append(
|
||||||
|
f"<div style='background: #fef2f2; border-left: 3px solid #dc2626; "
|
||||||
|
f"padding: 8px 12px; margin-bottom: 12px; font-size: 12px;'>"
|
||||||
|
f"<strong>Reason:</strong><br>{feedback_escaped}</div>"
|
||||||
|
)
|
||||||
elif state == "modified":
|
elif state == "modified":
|
||||||
lines.append(
|
lines.append(
|
||||||
"<p style='background: #feebc8; color: #c05621; padding: 6px 10px; "
|
"<p style='background: #feebc8; color: #c05621; padding: 6px 10px; "
|
||||||
|
|
@ -592,11 +710,13 @@ class ToolsPage(QWidget):
|
||||||
status_data = client.get_my_tool_status(tool_name)
|
status_data = client.get_my_tool_status(tool_name)
|
||||||
new_status = status_data.get("status", "pending")
|
new_status = status_data.get("status", "pending")
|
||||||
new_hash = status_data.get("config_hash")
|
new_hash = status_data.get("config_hash")
|
||||||
|
new_feedback = status_data.get("feedback")
|
||||||
|
|
||||||
# Update local config
|
# Update local config
|
||||||
config_data = yaml.safe_load(config_path.read_text()) or {}
|
config_data = yaml.safe_load(config_path.read_text()) or {}
|
||||||
old_status = config_data.get("registry_status", "pending")
|
old_status = config_data.get("registry_status", "pending")
|
||||||
old_hash = config_data.get("registry_hash")
|
old_hash = config_data.get("registry_hash")
|
||||||
|
old_feedback = config_data.get("registry_feedback")
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
if old_status != new_status:
|
if old_status != new_status:
|
||||||
|
|
@ -605,6 +725,12 @@ class ToolsPage(QWidget):
|
||||||
if new_hash and old_hash != new_hash:
|
if new_hash and old_hash != new_hash:
|
||||||
config_data["registry_hash"] = new_hash
|
config_data["registry_hash"] = new_hash
|
||||||
changed = True
|
changed = True
|
||||||
|
if new_feedback != old_feedback:
|
||||||
|
if new_feedback:
|
||||||
|
config_data["registry_feedback"] = new_feedback
|
||||||
|
elif "registry_feedback" in config_data:
|
||||||
|
del config_data["registry_feedback"]
|
||||||
|
changed = True
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
|
config_path.write_text(yaml.dump(config_data, default_flow_style=False, sort_keys=False))
|
||||||
|
|
@ -613,6 +739,8 @@ class ToolsPage(QWidget):
|
||||||
self.main_window.show_status(f"Tool '{tool_name}' has been approved!")
|
self.main_window.show_status(f"Tool '{tool_name}' has been approved!")
|
||||||
elif new_status == "rejected":
|
elif new_status == "rejected":
|
||||||
self.main_window.show_status(f"Tool '{tool_name}' was rejected")
|
self.main_window.show_status(f"Tool '{tool_name}' was rejected")
|
||||||
|
elif new_status == "changes_requested":
|
||||||
|
self.main_window.show_status(f"Changes requested for '{tool_name}' - see feedback")
|
||||||
else:
|
else:
|
||||||
self.main_window.show_status(f"Status updated: {new_status}")
|
self.main_window.show_status(f"Status updated: {new_status}")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<button onclick="approveTool({{ tool.id }})" class="text-green-600 hover:text-green-900 mr-2">Approve</button>
|
<button onclick="approveTool({{ tool.id }})" class="text-green-600 hover:text-green-900 mr-2">Approve</button>
|
||||||
<button onclick="requestChanges({{ tool.id }})" class="text-yellow-600 hover:text-yellow-900 mr-2">Changes</button>
|
<button onclick="requestChanges({{ tool.id }})" class="mr-2" style="color: #d97706;">Changes</button>
|
||||||
<button onclick="rejectTool({{ tool.id }})" class="text-red-600 hover:text-red-900">Reject</button>
|
<button onclick="rejectTool({{ tool.id }})" class="text-red-600 hover:text-red-900">Reject</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -194,7 +194,7 @@
|
||||||
<div class="px-6 py-4 flex justify-end space-x-3 border-t bg-gray-50 rounded-b-lg flex-shrink-0">
|
<div class="px-6 py-4 flex justify-end space-x-3 border-t bg-gray-50 rounded-b-lg flex-shrink-0">
|
||||||
<button onclick="closeDetailModal()" class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500">Close</button>
|
<button onclick="closeDetailModal()" class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500">Close</button>
|
||||||
<button id="detail-approve-btn" class="px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-md hover:bg-green-700">Approve</button>
|
<button id="detail-approve-btn" class="px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-md hover:bg-green-700">Approve</button>
|
||||||
<button id="detail-changes-btn" class="px-4 py-2 bg-yellow-500 text-white text-sm font-medium rounded-md hover:bg-yellow-600">Request Changes</button>
|
<button id="detail-changes-btn" class="px-4 py-2 border-2 border-yellow-500 text-yellow-700 text-sm font-medium rounded-md hover:bg-yellow-50" style="background-color: #fef3c7; border-color: #f59e0b; color: #92400e;">Request Changes</button>
|
||||||
<button id="detail-reject-btn" class="px-4 py-2 bg-red-600 text-white text-sm font-medium rounded-md hover:bg-red-700">Reject</button>
|
<button id="detail-reject-btn" class="px-4 py-2 bg-red-600 text-white text-sm font-medium rounded-md hover:bg-red-700">Reject</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -215,7 +215,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex justify-end space-x-3">
|
<div class="mt-4 flex justify-end space-x-3">
|
||||||
<button onclick="closeChangesModal()" class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500">Cancel</button>
|
<button onclick="closeChangesModal()" class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500">Cancel</button>
|
||||||
<button onclick="confirmRequestChanges()" class="px-4 py-2 bg-yellow-500 text-white text-sm font-medium rounded-md hover:bg-yellow-600">Send Feedback</button>
|
<button onclick="confirmRequestChanges()" class="px-4 py-2 text-sm font-medium rounded-md" style="background-color: #f59e0b; color: white;">Send Feedback</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue