This commit is contained in:
+34
-3
@@ -25,14 +25,45 @@ The expected flow is:
|
|||||||
1. ChatGPT calls `shd_list_projects` with `archived: false`.
|
1. ChatGPT calls `shd_list_projects` with `archived: false`.
|
||||||
2. ChatGPT may call a module render tool after the corresponding list tool:
|
2. ChatGPT may call a module render tool after the corresponding list tool:
|
||||||
`shd_render_projects_widget`, `shd_render_documents_widget`,
|
`shd_render_projects_widget`, `shd_render_documents_widget`,
|
||||||
`shd_render_tasks_widget`, `shd_render_finance_widget` or
|
`shd_render_tasks_widget`, `shd_render_finance_widget`,
|
||||||
`shd_render_crm_widget`.
|
`shd_render_crm_widget`, `shd_render_discussions_widget`,
|
||||||
|
`shd_render_notifications_widget`, `shd_render_scheduling_widget`,
|
||||||
|
`shd_render_inventory_widget`, `shd_render_agents_widget`,
|
||||||
|
`shd_render_status_page_widget`, `shd_render_project_db_widget` or
|
||||||
|
`shd_render_notes_widget`, `shd_render_project_overview_widget`,
|
||||||
|
`shd_render_files_widget`, `shd_render_proposals_widget`,
|
||||||
|
`shd_render_terms_widget`, `shd_render_activity_widget`,
|
||||||
|
`shd_render_organizations_widget`, `shd_render_gitea_widget` or
|
||||||
|
`shd_render_schemes_widget`, `shd_render_task_kanban_widget`,
|
||||||
|
`shd_render_team_workload_widget`, `shd_render_project_timeline_widget`,
|
||||||
|
`shd_render_crm_funnel_widget`, `shd_render_finance_dashboard_widget`,
|
||||||
|
`shd_render_proposal_approvals_widget`, `shd_render_documents_completeness_widget`,
|
||||||
|
`shd_render_agents_health_widget`, `shd_render_schedule_calendar_widget`,
|
||||||
|
`shd_render_acl_matrix_widget`, `shd_render_inventory_warnings_widget`,
|
||||||
|
`shd_render_gitea_board_widget`, `shd_render_schemes_progress_widget`,
|
||||||
|
`shd_render_notes_tree_widget` or `shd_render_project_db_preview_widget`.
|
||||||
3. The selected widget renders a read-only register and can call the matching
|
3. The selected widget renders a read-only register and can call the matching
|
||||||
detail tool for one selected row.
|
detail tool for one selected row.
|
||||||
|
|
||||||
The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
||||||
`ui://shd/documents/v1.html`, `ui://shd/tasks/v1.html`,
|
`ui://shd/documents/v1.html`, `ui://shd/tasks/v1.html`,
|
||||||
`ui://shd/finance/v1.html` and `ui://shd/crm/v1.html`.
|
`ui://shd/finance/v1.html`, `ui://shd/crm/v1.html`,
|
||||||
|
`ui://shd/discussions/v1.html`, `ui://shd/notifications/v1.html`,
|
||||||
|
`ui://shd/scheduling/v1.html`, `ui://shd/inventory/v1.html`,
|
||||||
|
`ui://shd/agents/v1.html`, `ui://shd/status-page/v1.html`,
|
||||||
|
`ui://shd/project-db/v1.html`, `ui://shd/notes/v1.html`,
|
||||||
|
`ui://shd/project-overview/v1.html`, `ui://shd/files/v1.html`,
|
||||||
|
`ui://shd/proposals/v1.html`, `ui://shd/terms/v1.html`,
|
||||||
|
`ui://shd/activity/v1.html`, `ui://shd/organizations/v1.html`,
|
||||||
|
`ui://shd/gitea/v1.html`, `ui://shd/schemes/v1.html`,
|
||||||
|
`ui://shd/task-kanban/v1.html`, `ui://shd/team-workload/v1.html`,
|
||||||
|
`ui://shd/project-timeline/v1.html`, `ui://shd/crm-funnel/v1.html`,
|
||||||
|
`ui://shd/finance-dashboard/v1.html`, `ui://shd/proposal-approvals/v1.html`,
|
||||||
|
`ui://shd/documents-completeness/v1.html`, `ui://shd/agents-health/v1.html`,
|
||||||
|
`ui://shd/schedule-calendar/v1.html`, `ui://shd/acl-matrix/v1.html`,
|
||||||
|
`ui://shd/inventory-warnings/v1.html`, `ui://shd/gitea-board/v1.html`,
|
||||||
|
`ui://shd/schemes-progress/v1.html`, `ui://shd/notes-tree/v1.html` and
|
||||||
|
`ui://shd/project-db-preview/v1.html`.
|
||||||
|
|
||||||
The MCP tools remain usable without the widget. A failed OAuth connection is
|
The MCP tools remain usable without the widget. A failed OAuth connection is
|
||||||
an account/endpoint configuration problem, not a missing token in this repo.
|
an account/endpoint configuration problem, not a missing token in this repo.
|
||||||
|
|||||||
@@ -63,12 +63,45 @@ The backend serves the checked-in widget artifact at
|
|||||||
to the HTML resource under `plugins/shd-mcp-plugin/widgets/active-projects/v1/`.
|
to the HTML resource under `plugins/shd-mcp-plugin/widgets/active-projects/v1/`.
|
||||||
|
|
||||||
The same read-only data/render contract is available for Documents, Tasks,
|
The same read-only data/render contract is available for Documents, Tasks,
|
||||||
Finance and CRM:
|
Finance, CRM, Discussions, Notifications, Scheduling, Inventory, Agents,
|
||||||
|
Status Page, Project DB, Notes, Project overview, Files, Proposals, Terms,
|
||||||
|
Activity, Organizations and access, Gitea Issues and Schemes:
|
||||||
|
|
||||||
- `shd_render_documents_widget` → `ui://shd/documents/v1.html`;
|
- `shd_render_documents_widget` → `ui://shd/documents/v1.html`;
|
||||||
- `shd_render_tasks_widget` → `ui://shd/tasks/v1.html`;
|
- `shd_render_tasks_widget` → `ui://shd/tasks/v1.html`;
|
||||||
- `shd_render_finance_widget` → `ui://shd/finance/v1.html`;
|
- `shd_render_finance_widget` → `ui://shd/finance/v1.html`;
|
||||||
- `shd_render_crm_widget` → `ui://shd/crm/v1.html`.
|
- `shd_render_crm_widget` → `ui://shd/crm/v1.html`;
|
||||||
|
- `shd_render_discussions_widget` → `ui://shd/discussions/v1.html`;
|
||||||
|
- `shd_render_notifications_widget` → `ui://shd/notifications/v1.html`;
|
||||||
|
- `shd_render_scheduling_widget` → `ui://shd/scheduling/v1.html`;
|
||||||
|
- `shd_render_inventory_widget` → `ui://shd/inventory/v1.html`;
|
||||||
|
- `shd_render_agents_widget` → `ui://shd/agents/v1.html`;
|
||||||
|
- `shd_render_status_page_widget` → `ui://shd/status-page/v1.html`;
|
||||||
|
- `shd_render_project_db_widget` → `ui://shd/project-db/v1.html`;
|
||||||
|
- `shd_render_notes_widget` → `ui://shd/notes/v1.html`.
|
||||||
|
- `shd_render_project_overview_widget` → `ui://shd/project-overview/v1.html`;
|
||||||
|
- `shd_render_files_widget` → `ui://shd/files/v1.html`;
|
||||||
|
- `shd_render_proposals_widget` → `ui://shd/proposals/v1.html`;
|
||||||
|
- `shd_render_terms_widget` → `ui://shd/terms/v1.html`;
|
||||||
|
- `shd_render_activity_widget` → `ui://shd/activity/v1.html`;
|
||||||
|
- `shd_render_organizations_widget` → `ui://shd/organizations/v1.html`;
|
||||||
|
- `shd_render_gitea_widget` → `ui://shd/gitea/v1.html`;
|
||||||
|
- `shd_render_schemes_widget` → `ui://shd/schemes/v1.html`;
|
||||||
|
- `shd_render_task_kanban_widget` → `ui://shd/task-kanban/v1.html`;
|
||||||
|
- `shd_render_team_workload_widget` → `ui://shd/team-workload/v1.html`;
|
||||||
|
- `shd_render_project_timeline_widget` → `ui://shd/project-timeline/v1.html`;
|
||||||
|
- `shd_render_crm_funnel_widget` → `ui://shd/crm-funnel/v1.html`;
|
||||||
|
- `shd_render_finance_dashboard_widget` → `ui://shd/finance-dashboard/v1.html`;
|
||||||
|
- `shd_render_proposal_approvals_widget` → `ui://shd/proposal-approvals/v1.html`;
|
||||||
|
- `shd_render_documents_completeness_widget` → `ui://shd/documents-completeness/v1.html`;
|
||||||
|
- `shd_render_agents_health_widget` → `ui://shd/agents-health/v1.html`;
|
||||||
|
- `shd_render_schedule_calendar_widget` → `ui://shd/schedule-calendar/v1.html`;
|
||||||
|
- `shd_render_acl_matrix_widget` → `ui://shd/acl-matrix/v1.html`;
|
||||||
|
- `shd_render_inventory_warnings_widget` → `ui://shd/inventory-warnings/v1.html`;
|
||||||
|
- `shd_render_gitea_board_widget` → `ui://shd/gitea-board/v1.html`;
|
||||||
|
- `shd_render_schemes_progress_widget` → `ui://shd/schemes-progress/v1.html`;
|
||||||
|
- `shd_render_notes_tree_widget` → `ui://shd/notes-tree/v1.html`;
|
||||||
|
- `shd_render_project_db_preview_widget` → `ui://shd/project-db-preview/v1.html`.
|
||||||
|
|
||||||
Each widget accepts normalized records from its list tool, keeps the plain
|
Each widget accepts normalized records from its list tool, keeps the plain
|
||||||
structured response as a fallback and uses the MCP Apps bridge before the
|
structured response as a fallback and uses the MCP Apps bridge before the
|
||||||
@@ -140,7 +173,7 @@ node /var/www/admin/scripts/mcp/mcp-catalog.mjs | \
|
|||||||
```
|
```
|
||||||
|
|
||||||
The runtime smoke harness checks OAuth metadata, PKCE S256 support, the
|
The runtime smoke harness checks OAuth metadata, PKCE S256 support, the
|
||||||
authenticated MCP handshake and all five widget resources. It never assumes
|
authenticated MCP handshake and all thirty-six widget resources. It never assumes
|
||||||
an endpoint or prints a token:
|
an endpoint or prints a token:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+4
-4
@@ -35,7 +35,7 @@ catalog is a checked-in name snapshot, so a Skill cannot silently reference an
|
|||||||
unpublished MCP tool. Refresh it from the backend catalog before publishing.
|
unpublished MCP tool. Refresh it from the backend catalog before publishing.
|
||||||
|
|
||||||
`scripts/mcp-runtime-smoke.mjs` checks OAuth metadata, authenticated MCP
|
`scripts/mcp-runtime-smoke.mjs` checks OAuth metadata, authenticated MCP
|
||||||
negotiation and the active-projects/Documents/Tasks/Finance/CRM resources.
|
negotiation and all thirty-six widget resources.
|
||||||
`scripts/mcp-oauth-e2e.mjs` is an opt-in Playwright check for the complete
|
`scripts/mcp-oauth-e2e.mjs` is an opt-in Playwright check for the complete
|
||||||
authorization-code + PKCE exchange using an already authenticated storage
|
authorization-code + PKCE exchange using an already authenticated storage
|
||||||
state. Neither script stores credentials or belongs in the static CI job.
|
state. Neither script stores credentials or belongs in the static CI job.
|
||||||
@@ -52,9 +52,9 @@ JWKS signature and rejects a repeated `jti`.
|
|||||||
used by `SHD_MCP_WIDGET_ROOT`, or at the repository path documented by the
|
used by `SHD_MCP_WIDGET_ROOT`, or at the repository path documented by the
|
||||||
backend loader.
|
backend loader.
|
||||||
3. Run the backend MCP verification and a real authenticated OAuth/tool call.
|
3. Run the backend MCP verification and a real authenticated OAuth/tool call.
|
||||||
4. Reconnect the custom ChatGPT app and test active projects, Documents, Tasks,
|
4. Reconnect the custom ChatGPT app and test all thirty-six widgets, including
|
||||||
Finance and CRM, including the plain structured fallback and each widget’s
|
the plain structured fallback and each widget’s refresh/detail action where
|
||||||
refresh/detail action.
|
supported.
|
||||||
5. Record the exact revision and any runtime/browser checks. Static tests do
|
5. Record the exact revision and any runtime/browser checks. Static tests do
|
||||||
not prove OAuth, ACLs, production hosting or ChatGPT rendering.
|
not prove OAuth, ACLs, production hosting or ChatGPT rendering.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,787 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"source": "SHD backend scripts/mcp/mcp-catalog.mjs",
|
||||||
|
"tools": [
|
||||||
|
"shd_activate_proposal_variant",
|
||||||
|
"shd_add_asset_maintenance",
|
||||||
|
"shd_add_crm_activity",
|
||||||
|
"shd_add_crm_note",
|
||||||
|
"shd_add_discussion_message",
|
||||||
|
"shd_add_finance_document_item",
|
||||||
|
"shd_add_organization_member",
|
||||||
|
"shd_add_procurement_items",
|
||||||
|
"shd_add_stocktake_results",
|
||||||
|
"shd_add_task_comment",
|
||||||
|
"shd_add_task_dependency",
|
||||||
|
"shd_add_task_project_member",
|
||||||
|
"shd_add_task_time_entry",
|
||||||
|
"shd_add_terms_point",
|
||||||
|
"shd_add_terms_room",
|
||||||
|
"shd_adjust_inventory_balance",
|
||||||
|
"shd_admin_controller_action",
|
||||||
|
"shd_admin_diagnostics",
|
||||||
|
"shd_admin_get_access_settings",
|
||||||
|
"shd_admin_get_module_settings",
|
||||||
|
"shd_admin_get_module_widget_layout",
|
||||||
|
"shd_admin_get_organization_members",
|
||||||
|
"shd_admin_get_project_settings",
|
||||||
|
"shd_admin_list_integrations",
|
||||||
|
"shd_admin_list_organizations",
|
||||||
|
"shd_admin_list_public_pages",
|
||||||
|
"shd_admin_list_users",
|
||||||
|
"shd_admin_save_organization",
|
||||||
|
"shd_admin_save_public_page",
|
||||||
|
"shd_admin_update_integration",
|
||||||
|
"shd_admin_update_module_settings",
|
||||||
|
"shd_admin_update_module_widget_layout",
|
||||||
|
"shd_admin_update_project_settings",
|
||||||
|
"shd_admin_update_user",
|
||||||
|
"shd_agents_access_rules",
|
||||||
|
"shd_agents_auto_exit_network_route",
|
||||||
|
"shd_agents_close_tunnel",
|
||||||
|
"shd_agents_create_controller_ssh_tunnel",
|
||||||
|
"shd_agents_create_controller_tcp_tunnel",
|
||||||
|
"shd_agents_create_tunnel",
|
||||||
|
"shd_agents_create_user_agent_tcp_tunnel",
|
||||||
|
"shd_agents_delete_exit_network_route_policy",
|
||||||
|
"shd_agents_drain_exit_node",
|
||||||
|
"shd_agents_enable_exit_node",
|
||||||
|
"shd_agents_exit_network",
|
||||||
|
"shd_agents_exit_network_route_policies",
|
||||||
|
"shd_agents_exit_node_logs",
|
||||||
|
"shd_agents_force_exit_network_route",
|
||||||
|
"shd_agents_get_async_status",
|
||||||
|
"shd_agents_get_controller_access_policies",
|
||||||
|
"shd_agents_get_controller_diagnostics",
|
||||||
|
"shd_agents_get_diagnostics",
|
||||||
|
"shd_agents_get_speed_test_status",
|
||||||
|
"shd_agents_get_user_agent_access_policies",
|
||||||
|
"shd_agents_history",
|
||||||
|
"shd_agents_overview",
|
||||||
|
"shd_agents_publish_controller_endpoint",
|
||||||
|
"shd_agents_publish_user_agent_endpoint",
|
||||||
|
"shd_agents_reboot_user_agent",
|
||||||
|
"shd_agents_replace_controller_access_policies",
|
||||||
|
"shd_agents_replace_user_agent_access_policies",
|
||||||
|
"shd_agents_restart_exit_node",
|
||||||
|
"shd_agents_retry_async_action",
|
||||||
|
"shd_agents_revoke_controller_endpoint",
|
||||||
|
"shd_agents_revoke_user_agent_endpoint",
|
||||||
|
"shd_agents_routing_gateway_command",
|
||||||
|
"shd_agents_save_access_rules",
|
||||||
|
"shd_agents_save_exit_network_route_policy",
|
||||||
|
"shd_agents_set_controller_zabbix_monitoring",
|
||||||
|
"shd_agents_set_user_agent_zabbix_monitoring",
|
||||||
|
"shd_agents_start_async_action",
|
||||||
|
"shd_agents_start_controller_speed_test",
|
||||||
|
"shd_agents_start_user_agent_speed_test",
|
||||||
|
"shd_agents_update_alice_runtime_settings",
|
||||||
|
"shd_agents_update_controller_settings",
|
||||||
|
"shd_agents_update_exit_node_config",
|
||||||
|
"shd_agents_update_user_agent",
|
||||||
|
"shd_agents_update_user_agent_settings",
|
||||||
|
"shd_allocate_finance_payment",
|
||||||
|
"shd_apply_note_operations",
|
||||||
|
"shd_apply_proposal_template_migration",
|
||||||
|
"shd_apply_proposal_template_migration_batch",
|
||||||
|
"shd_apply_scheme_operations",
|
||||||
|
"shd_apply_stocktake_adjustments",
|
||||||
|
"shd_approve_procurement_request",
|
||||||
|
"shd_archive_agent",
|
||||||
|
"shd_archive_crm_entity",
|
||||||
|
"shd_archive_discussion_channel",
|
||||||
|
"shd_archive_discussion_topic",
|
||||||
|
"shd_archive_note_collection",
|
||||||
|
"shd_archive_organization",
|
||||||
|
"shd_archive_project",
|
||||||
|
"shd_archive_task",
|
||||||
|
"shd_archive_task_automation_rule",
|
||||||
|
"shd_archive_task_form",
|
||||||
|
"shd_archive_task_portfolio_automation_rule",
|
||||||
|
"shd_assign_inventory_asset",
|
||||||
|
"shd_assign_task_to_sprint",
|
||||||
|
"shd_attach_file_to_entity",
|
||||||
|
"shd_bulk_assign_crm_responsible",
|
||||||
|
"shd_bulk_move_crm_stage",
|
||||||
|
"shd_bulk_replace_finance_document_items",
|
||||||
|
"shd_bulk_update_crm_entities",
|
||||||
|
"shd_bulk_update_tasks",
|
||||||
|
"shd_bulk_upsert_inventory_items",
|
||||||
|
"shd_bulk_upsert_terms_points",
|
||||||
|
"shd_calculate_proposal_template_version",
|
||||||
|
"shd_cancel_discussion_message_reminder",
|
||||||
|
"shd_cancel_organization_invitation",
|
||||||
|
"shd_cancel_scheduled_discussion_message",
|
||||||
|
"shd_cancel_scheduling_booking",
|
||||||
|
"shd_capabilities",
|
||||||
|
"shd_close_asset_maintenance",
|
||||||
|
"shd_close_inventory_stocktake",
|
||||||
|
"shd_comment_gitea_issue",
|
||||||
|
"shd_compare_terms_versions",
|
||||||
|
"shd_complete_crm_activity",
|
||||||
|
"shd_complete_crm_reminder",
|
||||||
|
"shd_complete_stocktake",
|
||||||
|
"shd_confirm_scheduling_booking",
|
||||||
|
"shd_convert_crm_lead",
|
||||||
|
"shd_copy_file",
|
||||||
|
"shd_create_controller",
|
||||||
|
"shd_create_crm_activity",
|
||||||
|
"shd_create_crm_lead",
|
||||||
|
"shd_create_crm_note",
|
||||||
|
"shd_create_crm_reminder",
|
||||||
|
"shd_create_discussion_channel",
|
||||||
|
"shd_create_discussion_topic",
|
||||||
|
"shd_create_document_external_link",
|
||||||
|
"shd_create_document_public_link",
|
||||||
|
"shd_create_document_template",
|
||||||
|
"shd_create_file_action",
|
||||||
|
"shd_create_file_share_link",
|
||||||
|
"shd_create_file_zip_link",
|
||||||
|
"shd_create_folder",
|
||||||
|
"shd_create_gitea_issue",
|
||||||
|
"shd_create_inventory_resource",
|
||||||
|
"shd_create_note_collection",
|
||||||
|
"shd_create_note_database",
|
||||||
|
"shd_create_note_database_property",
|
||||||
|
"shd_create_note_database_record",
|
||||||
|
"shd_create_note_database_view",
|
||||||
|
"shd_create_note_page",
|
||||||
|
"shd_create_note_page_discussion",
|
||||||
|
"shd_create_note_page_from_template",
|
||||||
|
"shd_create_note_review_comment",
|
||||||
|
"shd_create_note_review_discussion",
|
||||||
|
"shd_create_note_space",
|
||||||
|
"shd_create_note_template",
|
||||||
|
"shd_create_organization",
|
||||||
|
"shd_create_procurement_request",
|
||||||
|
"shd_create_procurement_shipment",
|
||||||
|
"shd_create_project",
|
||||||
|
"shd_create_project_device",
|
||||||
|
"shd_create_project_service",
|
||||||
|
"shd_create_proposal",
|
||||||
|
"shd_create_proposal_documents_contract",
|
||||||
|
"shd_create_proposal_template_version",
|
||||||
|
"shd_create_proposal_variant",
|
||||||
|
"shd_create_scheduling_availability_rule",
|
||||||
|
"shd_create_scheduling_booking",
|
||||||
|
"shd_create_scheduling_booking_link",
|
||||||
|
"shd_create_scheduling_event_type",
|
||||||
|
"shd_create_scheme_description_comment",
|
||||||
|
"shd_create_scheme_document",
|
||||||
|
"shd_create_status_page_group",
|
||||||
|
"shd_create_status_page_monitor",
|
||||||
|
"shd_create_stocktake",
|
||||||
|
"shd_create_task",
|
||||||
|
"shd_create_task_approval_route",
|
||||||
|
"shd_create_task_attachment_annotation",
|
||||||
|
"shd_create_task_automation_rule",
|
||||||
|
"shd_create_task_checklist_item",
|
||||||
|
"shd_create_task_custom_field",
|
||||||
|
"shd_create_task_form",
|
||||||
|
"shd_create_task_goal",
|
||||||
|
"shd_create_task_portfolio",
|
||||||
|
"shd_create_task_portfolio_automation_rule",
|
||||||
|
"shd_create_task_portfolio_member",
|
||||||
|
"shd_create_task_portfolio_status_update",
|
||||||
|
"shd_create_task_project",
|
||||||
|
"shd_create_task_project_announcement",
|
||||||
|
"shd_create_task_project_member",
|
||||||
|
"shd_create_task_project_status_update",
|
||||||
|
"shd_create_task_sprint",
|
||||||
|
"shd_create_task_status",
|
||||||
|
"shd_create_task_tag",
|
||||||
|
"shd_create_task_template",
|
||||||
|
"shd_create_task_view",
|
||||||
|
"shd_create_terms_document",
|
||||||
|
"shd_crm_workflow",
|
||||||
|
"shd_decide_proposal_approval",
|
||||||
|
"shd_delete_agent",
|
||||||
|
"shd_delete_controller",
|
||||||
|
"shd_delete_crm_reminder",
|
||||||
|
"shd_delete_discussion_message",
|
||||||
|
"shd_delete_discussion_topic",
|
||||||
|
"shd_delete_discussion_topic_draft",
|
||||||
|
"shd_delete_document_comment",
|
||||||
|
"shd_delete_document_external_link",
|
||||||
|
"shd_delete_document_template",
|
||||||
|
"shd_delete_exchange_rate",
|
||||||
|
"shd_delete_finance_account",
|
||||||
|
"shd_delete_finance_attachment",
|
||||||
|
"shd_delete_finance_category",
|
||||||
|
"shd_delete_finance_counterparty",
|
||||||
|
"shd_delete_finance_document_item",
|
||||||
|
"shd_delete_finance_legal_entity",
|
||||||
|
"shd_delete_finance_payment",
|
||||||
|
"shd_delete_finance_project",
|
||||||
|
"shd_delete_inventory_item",
|
||||||
|
"shd_delete_note_attachment",
|
||||||
|
"shd_delete_note_database",
|
||||||
|
"shd_delete_note_database_property",
|
||||||
|
"shd_delete_note_database_record",
|
||||||
|
"shd_delete_note_database_view",
|
||||||
|
"shd_delete_note_space",
|
||||||
|
"shd_delete_project_device",
|
||||||
|
"shd_delete_project_service",
|
||||||
|
"shd_delete_proposal",
|
||||||
|
"shd_delete_proposal_template_library_item",
|
||||||
|
"shd_delete_scheduling_availability_rule",
|
||||||
|
"shd_delete_scheduling_event_type",
|
||||||
|
"shd_delete_scheme_document",
|
||||||
|
"shd_delete_status_page_group",
|
||||||
|
"shd_delete_status_page_monitor",
|
||||||
|
"shd_delete_task",
|
||||||
|
"shd_delete_task_attachment",
|
||||||
|
"shd_delete_task_attachment_annotation",
|
||||||
|
"shd_delete_task_checklist_item",
|
||||||
|
"shd_delete_task_custom_field",
|
||||||
|
"shd_delete_task_dependency",
|
||||||
|
"shd_delete_task_goal",
|
||||||
|
"shd_delete_task_import_template",
|
||||||
|
"shd_delete_task_portfolio_member",
|
||||||
|
"shd_delete_task_project_announcement",
|
||||||
|
"shd_delete_task_project_member",
|
||||||
|
"shd_delete_task_status",
|
||||||
|
"shd_delete_task_tag",
|
||||||
|
"shd_delete_task_template",
|
||||||
|
"shd_delete_task_view",
|
||||||
|
"shd_delete_terms_document",
|
||||||
|
"shd_delete_terms_point",
|
||||||
|
"shd_delete_terms_room",
|
||||||
|
"shd_diff_note_revisions",
|
||||||
|
"shd_disconnect_agent",
|
||||||
|
"shd_disconnect_scheduling_calendar",
|
||||||
|
"shd_disconnect_scheduling_meeting_account",
|
||||||
|
"shd_document_contract_workflow",
|
||||||
|
"shd_download_document_pdf",
|
||||||
|
"shd_download_finance_attachment",
|
||||||
|
"shd_download_note_attachment",
|
||||||
|
"shd_download_proposal_pdf",
|
||||||
|
"shd_download_scheme_pdf",
|
||||||
|
"shd_download_terms_pdf",
|
||||||
|
"shd_duplicate_document_template",
|
||||||
|
"shd_duplicate_note_page",
|
||||||
|
"shd_duplicate_proposal",
|
||||||
|
"shd_duplicate_scheme_document",
|
||||||
|
"shd_duplicate_task",
|
||||||
|
"shd_duplicate_task_automation_rule",
|
||||||
|
"shd_duplicate_task_portfolio_automation_rule",
|
||||||
|
"shd_duplicate_terms_document",
|
||||||
|
"shd_export_finance_csv",
|
||||||
|
"shd_export_finance_document_pdf",
|
||||||
|
"shd_export_note_markdown",
|
||||||
|
"shd_export_scheme_document",
|
||||||
|
"shd_export_task_time_report",
|
||||||
|
"shd_finance_document_action",
|
||||||
|
"shd_finance_presence_leave",
|
||||||
|
"shd_finance_presence_upsert",
|
||||||
|
"shd_generate_contract_from_template",
|
||||||
|
"shd_generate_document_pdf",
|
||||||
|
"shd_generate_terms_pdf",
|
||||||
|
"shd_get_agent_notifications",
|
||||||
|
"shd_get_agent_settings",
|
||||||
|
"shd_get_alice_devices",
|
||||||
|
"shd_get_alice_diagnostics",
|
||||||
|
"shd_get_alice_selected_devices",
|
||||||
|
"shd_get_alice_settings",
|
||||||
|
"shd_get_audit_history",
|
||||||
|
"shd_get_contract",
|
||||||
|
"shd_get_controller_status",
|
||||||
|
"shd_get_crm_entity",
|
||||||
|
"shd_get_current_user",
|
||||||
|
"shd_get_document_contract_config",
|
||||||
|
"shd_get_document_render_preview",
|
||||||
|
"shd_get_file_link",
|
||||||
|
"shd_get_file_metadata",
|
||||||
|
"shd_get_file_versions",
|
||||||
|
"shd_get_finance_account_balance",
|
||||||
|
"shd_get_finance_document",
|
||||||
|
"shd_get_finance_import_errors",
|
||||||
|
"shd_get_finance_payment",
|
||||||
|
"shd_get_finance_settings",
|
||||||
|
"shd_get_grafana_settings",
|
||||||
|
"shd_get_grafana_settings_schema",
|
||||||
|
"shd_get_homekit_inventory",
|
||||||
|
"shd_get_homekit_selected_devices",
|
||||||
|
"shd_get_homekit_state",
|
||||||
|
"shd_get_homekit_templates",
|
||||||
|
"shd_get_inventory_asset",
|
||||||
|
"shd_get_inventory_item",
|
||||||
|
"shd_get_inventory_procurement",
|
||||||
|
"shd_get_note_backlinks",
|
||||||
|
"shd_get_note_database",
|
||||||
|
"shd_get_note_external_reference",
|
||||||
|
"shd_get_note_page",
|
||||||
|
"shd_get_note_revision",
|
||||||
|
"shd_get_note_space_tree",
|
||||||
|
"shd_get_note_template",
|
||||||
|
"shd_get_notification_preferences",
|
||||||
|
"shd_get_notification_read_state",
|
||||||
|
"shd_get_organization_two_factor_policy",
|
||||||
|
"shd_get_project",
|
||||||
|
"shd_get_project_context",
|
||||||
|
"shd_get_project_delete_plan",
|
||||||
|
"shd_get_project_settings",
|
||||||
|
"shd_get_proposal",
|
||||||
|
"shd_get_proposal_approval",
|
||||||
|
"shd_get_proposal_public_analytics",
|
||||||
|
"shd_get_proposal_public_links",
|
||||||
|
"shd_get_proposal_settings",
|
||||||
|
"shd_get_proposal_template",
|
||||||
|
"shd_get_proposal_template_capabilities",
|
||||||
|
"shd_get_proposal_template_migration_report",
|
||||||
|
"shd_get_proposal_template_version",
|
||||||
|
"shd_get_realtime_events",
|
||||||
|
"shd_get_scheduling_oauth_links",
|
||||||
|
"shd_get_scheduling_oauth_status",
|
||||||
|
"shd_get_scheduling_overview",
|
||||||
|
"shd_get_scheme_changes",
|
||||||
|
"shd_get_scheme_description_template",
|
||||||
|
"shd_get_scheme_document",
|
||||||
|
"shd_get_scheme_events",
|
||||||
|
"shd_get_scheme_log",
|
||||||
|
"shd_get_scheme_point",
|
||||||
|
"shd_get_scheme_point_discussion",
|
||||||
|
"shd_get_scheme_point_discussions",
|
||||||
|
"shd_get_status_page_monitor_history",
|
||||||
|
"shd_get_status_page_monitors",
|
||||||
|
"shd_get_task",
|
||||||
|
"shd_get_task_budget_summary",
|
||||||
|
"shd_get_task_capacity",
|
||||||
|
"shd_get_task_form",
|
||||||
|
"shd_get_task_project_meta",
|
||||||
|
"shd_get_task_time_report",
|
||||||
|
"shd_get_task_utilization",
|
||||||
|
"shd_get_task_workload",
|
||||||
|
"shd_get_terms_completeness",
|
||||||
|
"shd_get_terms_document",
|
||||||
|
"shd_get_terms_document_completeness",
|
||||||
|
"shd_get_terms_document_revision",
|
||||||
|
"shd_get_voice_agent_config",
|
||||||
|
"shd_get_voice_agent_diagnostics",
|
||||||
|
"shd_get_voice_agent_status",
|
||||||
|
"shd_grant_note_member",
|
||||||
|
"shd_hide_discussion_channel",
|
||||||
|
"shd_hold_scheduling_slot",
|
||||||
|
"shd_import_finance_specification",
|
||||||
|
"shd_import_note_markdown",
|
||||||
|
"shd_import_note_pages",
|
||||||
|
"shd_install_homekit",
|
||||||
|
"shd_install_task_automation_bundle",
|
||||||
|
"shd_install_task_automation_template",
|
||||||
|
"shd_inventory_action",
|
||||||
|
"shd_invite_user_to_create_organization",
|
||||||
|
"shd_invite_user_to_organization",
|
||||||
|
"shd_leave_note_space_presence",
|
||||||
|
"shd_leave_task_project_presence",
|
||||||
|
"shd_link_crm_entities",
|
||||||
|
"shd_list_contracts",
|
||||||
|
"shd_list_crm",
|
||||||
|
"shd_list_crm_activities",
|
||||||
|
"shd_list_crm_audit_log",
|
||||||
|
"shd_list_crm_custom_field_values",
|
||||||
|
"shd_list_crm_custom_fields",
|
||||||
|
"shd_list_crm_entity_links",
|
||||||
|
"shd_list_crm_loss_reasons",
|
||||||
|
"shd_list_crm_notes",
|
||||||
|
"shd_list_crm_pipelines",
|
||||||
|
"shd_list_crm_reminders",
|
||||||
|
"shd_list_crm_saved_views",
|
||||||
|
"shd_list_discussion_channels",
|
||||||
|
"shd_list_discussion_mention_users",
|
||||||
|
"shd_list_discussion_messages",
|
||||||
|
"shd_list_discussion_topics",
|
||||||
|
"shd_list_document_comments",
|
||||||
|
"shd_list_document_external_links",
|
||||||
|
"shd_list_document_notifications",
|
||||||
|
"shd_list_document_public_links",
|
||||||
|
"shd_list_document_revisions",
|
||||||
|
"shd_list_document_templates",
|
||||||
|
"shd_list_files",
|
||||||
|
"shd_list_finance_audit_log",
|
||||||
|
"shd_list_finance_documents",
|
||||||
|
"shd_list_finance_exchange_rates",
|
||||||
|
"shd_list_finance_import_jobs",
|
||||||
|
"shd_list_finance_payments",
|
||||||
|
"shd_list_finance_public_links",
|
||||||
|
"shd_list_finance_realtime_events",
|
||||||
|
"shd_list_finance_refs",
|
||||||
|
"shd_list_gitea_issues",
|
||||||
|
"shd_list_gitea_labels",
|
||||||
|
"shd_list_inventory_asset_assignments",
|
||||||
|
"shd_list_inventory_asset_maintenance",
|
||||||
|
"shd_list_inventory_assets",
|
||||||
|
"shd_list_inventory_audit_log",
|
||||||
|
"shd_list_inventory_items",
|
||||||
|
"shd_list_inventory_labels",
|
||||||
|
"shd_list_inventory_locations",
|
||||||
|
"shd_list_inventory_lots",
|
||||||
|
"shd_list_inventory_movements",
|
||||||
|
"shd_list_inventory_procurement",
|
||||||
|
"shd_list_inventory_procurement_shipments",
|
||||||
|
"shd_list_inventory_public_links",
|
||||||
|
"shd_list_inventory_receipts",
|
||||||
|
"shd_list_inventory_reservations",
|
||||||
|
"shd_list_inventory_stocktakes",
|
||||||
|
"shd_list_note_attachments",
|
||||||
|
"shd_list_note_collections",
|
||||||
|
"shd_list_note_databases",
|
||||||
|
"shd_list_note_member_candidates",
|
||||||
|
"shd_list_note_members",
|
||||||
|
"shd_list_note_operations",
|
||||||
|
"shd_list_note_pages",
|
||||||
|
"shd_list_note_public_links",
|
||||||
|
"shd_list_note_review_comments",
|
||||||
|
"shd_list_note_revisions",
|
||||||
|
"shd_list_note_spaces",
|
||||||
|
"shd_list_note_templates",
|
||||||
|
"shd_list_notification_modules",
|
||||||
|
"shd_list_notifications",
|
||||||
|
"shd_list_organization_audit",
|
||||||
|
"shd_list_organization_invitations",
|
||||||
|
"shd_list_organization_members",
|
||||||
|
"shd_list_projects",
|
||||||
|
"shd_list_proposal_template_library",
|
||||||
|
"shd_list_proposal_template_versions",
|
||||||
|
"shd_list_proposal_templates",
|
||||||
|
"shd_list_proposal_variants",
|
||||||
|
"shd_list_proposal_versions",
|
||||||
|
"shd_list_proposals",
|
||||||
|
"shd_list_scheduling_availability",
|
||||||
|
"shd_list_scheduling_booking_links",
|
||||||
|
"shd_list_scheduling_bookings",
|
||||||
|
"shd_list_scheduling_event_types",
|
||||||
|
"shd_list_scheduling_integrations",
|
||||||
|
"shd_list_scheduling_slots",
|
||||||
|
"shd_list_scheme_documents",
|
||||||
|
"shd_list_scheme_electrical_points",
|
||||||
|
"shd_list_task_activity",
|
||||||
|
"shd_list_task_approval_routes",
|
||||||
|
"shd_list_task_attachment_annotations",
|
||||||
|
"shd_list_task_attachments",
|
||||||
|
"shd_list_task_audit",
|
||||||
|
"shd_list_task_automation_bundles",
|
||||||
|
"shd_list_task_automation_logs",
|
||||||
|
"shd_list_task_automation_rules",
|
||||||
|
"shd_list_task_automation_templates",
|
||||||
|
"shd_list_task_checklists",
|
||||||
|
"shd_list_task_comments",
|
||||||
|
"shd_list_task_dependencies",
|
||||||
|
"shd_list_task_form_submissions",
|
||||||
|
"shd_list_task_forms",
|
||||||
|
"shd_list_task_goals",
|
||||||
|
"shd_list_task_portfolio_automation_logs",
|
||||||
|
"shd_list_task_portfolio_automation_rules",
|
||||||
|
"shd_list_task_portfolio_members",
|
||||||
|
"shd_list_task_portfolio_status_updates",
|
||||||
|
"shd_list_task_portfolios",
|
||||||
|
"shd_list_task_project_announcements",
|
||||||
|
"shd_list_task_project_members",
|
||||||
|
"shd_list_task_project_status_updates",
|
||||||
|
"shd_list_task_projects",
|
||||||
|
"shd_list_task_templates",
|
||||||
|
"shd_list_task_time_entries",
|
||||||
|
"shd_list_task_timesheets",
|
||||||
|
"shd_list_task_view_shares",
|
||||||
|
"shd_list_tasks",
|
||||||
|
"shd_list_terms_document_log",
|
||||||
|
"shd_list_terms_document_ops",
|
||||||
|
"shd_list_terms_document_revisions",
|
||||||
|
"shd_list_terms_documents",
|
||||||
|
"shd_mark_all_notifications_read",
|
||||||
|
"shd_mark_crm_lead_lost",
|
||||||
|
"shd_mark_notification_read",
|
||||||
|
"shd_mark_notifications_read",
|
||||||
|
"shd_move_crm_deal_stage",
|
||||||
|
"shd_move_discussion_message",
|
||||||
|
"shd_move_inventory_stock",
|
||||||
|
"shd_move_note_collection",
|
||||||
|
"shd_move_task",
|
||||||
|
"shd_mutate_scheme_point",
|
||||||
|
"shd_note_page_action",
|
||||||
|
"shd_pause_task_timer",
|
||||||
|
"shd_pin_discussion_message",
|
||||||
|
"shd_preview_finance_import",
|
||||||
|
"shd_preview_project_db_csv_import",
|
||||||
|
"shd_preview_proposal_template",
|
||||||
|
"shd_preview_task_automation_rule",
|
||||||
|
"shd_preview_task_portfolio_automation_rule",
|
||||||
|
"shd_project_base_access_settings",
|
||||||
|
"shd_project_base_access_settings_update",
|
||||||
|
"shd_project_base_archive",
|
||||||
|
"shd_project_base_attach",
|
||||||
|
"shd_project_base_catalog",
|
||||||
|
"shd_project_base_create",
|
||||||
|
"shd_project_base_delete",
|
||||||
|
"shd_project_base_detach",
|
||||||
|
"shd_project_base_invite",
|
||||||
|
"shd_project_base_invite_revoke",
|
||||||
|
"shd_project_base_invites",
|
||||||
|
"shd_project_base_member_grant",
|
||||||
|
"shd_project_base_member_revoke",
|
||||||
|
"shd_project_base_member_update",
|
||||||
|
"shd_project_base_members",
|
||||||
|
"shd_project_base_metadata",
|
||||||
|
"shd_project_base_owner_update",
|
||||||
|
"shd_project_base_provisioning_retry",
|
||||||
|
"shd_project_base_provisioning_status",
|
||||||
|
"shd_project_base_public_link",
|
||||||
|
"shd_project_base_public_link_get",
|
||||||
|
"shd_project_base_public_link_revoke",
|
||||||
|
"shd_project_base_restore",
|
||||||
|
"shd_project_base_update",
|
||||||
|
"shd_project_db_action",
|
||||||
|
"shd_project_db_action_schema",
|
||||||
|
"shd_project_db_batch_rollback",
|
||||||
|
"shd_project_db_bulk_create_records",
|
||||||
|
"shd_project_db_bulk_upsert_records",
|
||||||
|
"shd_project_db_create_record",
|
||||||
|
"shd_project_db_delete_record",
|
||||||
|
"shd_project_db_import_csv",
|
||||||
|
"shd_project_db_list_attachments",
|
||||||
|
"shd_project_db_record_history",
|
||||||
|
"shd_project_db_records",
|
||||||
|
"shd_project_db_remove_attachment",
|
||||||
|
"shd_project_db_resolve_linked_records",
|
||||||
|
"shd_project_db_restore_record_version",
|
||||||
|
"shd_project_db_schema",
|
||||||
|
"shd_project_db_tables",
|
||||||
|
"shd_project_db_update_record",
|
||||||
|
"shd_project_db_upload_attachment",
|
||||||
|
"shd_project_db_validate_records",
|
||||||
|
"shd_proposal_template_version_workflow",
|
||||||
|
"shd_proposal_template_workflow",
|
||||||
|
"shd_proposal_workflow",
|
||||||
|
"shd_publish_discussion_note",
|
||||||
|
"shd_publish_document_public_link",
|
||||||
|
"shd_publish_proposal",
|
||||||
|
"shd_publish_scheme_point_discussion_note",
|
||||||
|
"shd_publish_status_page_incident",
|
||||||
|
"shd_publish_task_form",
|
||||||
|
"shd_react_to_discussion_message",
|
||||||
|
"shd_read_note_document",
|
||||||
|
"shd_reassign_task_resource",
|
||||||
|
"shd_reboot_agent",
|
||||||
|
"shd_receive_inventory_items",
|
||||||
|
"shd_receive_inventory_stock",
|
||||||
|
"shd_receive_procurement_shipment",
|
||||||
|
"shd_release_inventory_reservation",
|
||||||
|
"shd_release_inventory_stock",
|
||||||
|
"shd_release_scheduling_slot_hold",
|
||||||
|
"shd_remove_finance_payment_allocation",
|
||||||
|
"shd_remove_organization_member",
|
||||||
|
"shd_remove_task_project_member",
|
||||||
|
"shd_rename_scheme_document",
|
||||||
|
"shd_render_acl_matrix_widget",
|
||||||
|
"shd_render_activity_widget",
|
||||||
|
"shd_render_agents_health_widget",
|
||||||
|
"shd_render_agents_widget",
|
||||||
|
"shd_render_crm_funnel_widget",
|
||||||
|
"shd_render_crm_widget",
|
||||||
|
"shd_render_discussions_widget",
|
||||||
|
"shd_render_documents_completeness_widget",
|
||||||
|
"shd_render_documents_widget",
|
||||||
|
"shd_render_files_widget",
|
||||||
|
"shd_render_finance_dashboard_widget",
|
||||||
|
"shd_render_finance_widget",
|
||||||
|
"shd_render_gitea_board_widget",
|
||||||
|
"shd_render_gitea_widget",
|
||||||
|
"shd_render_inventory_warnings_widget",
|
||||||
|
"shd_render_inventory_widget",
|
||||||
|
"shd_render_notes_tree_widget",
|
||||||
|
"shd_render_notes_widget",
|
||||||
|
"shd_render_notifications_widget",
|
||||||
|
"shd_render_organizations_widget",
|
||||||
|
"shd_render_project_db_preview_widget",
|
||||||
|
"shd_render_project_db_widget",
|
||||||
|
"shd_render_project_overview_widget",
|
||||||
|
"shd_render_project_timeline_widget",
|
||||||
|
"shd_render_projects_widget",
|
||||||
|
"shd_render_proposal_approvals_widget",
|
||||||
|
"shd_render_proposals_widget",
|
||||||
|
"shd_render_schedule_calendar_widget",
|
||||||
|
"shd_render_scheduling_widget",
|
||||||
|
"shd_render_schemes_progress_widget",
|
||||||
|
"shd_render_schemes_widget",
|
||||||
|
"shd_render_status_page_widget",
|
||||||
|
"shd_render_task_kanban_widget",
|
||||||
|
"shd_render_tasks_widget",
|
||||||
|
"shd_render_team_workload_widget",
|
||||||
|
"shd_render_terms_widget",
|
||||||
|
"shd_replace_file",
|
||||||
|
"shd_replace_note_document",
|
||||||
|
"shd_replace_proposal_items",
|
||||||
|
"shd_request_proposal_approval",
|
||||||
|
"shd_request_task_approval",
|
||||||
|
"shd_reschedule_scheduling_booking",
|
||||||
|
"shd_resend_organization_invitation",
|
||||||
|
"shd_reserve_inventory_items",
|
||||||
|
"shd_reserve_inventory_stock",
|
||||||
|
"shd_resolve_discussion_topic",
|
||||||
|
"shd_resolve_document_comment",
|
||||||
|
"shd_resolve_scheme_description_comment",
|
||||||
|
"shd_resolve_scheme_point_discussion",
|
||||||
|
"shd_resolve_task_approval",
|
||||||
|
"shd_resolve_task_attachment_annotation",
|
||||||
|
"shd_restart_homekit",
|
||||||
|
"shd_restore_discussion_channel",
|
||||||
|
"shd_restore_discussion_topic",
|
||||||
|
"shd_restore_file_version",
|
||||||
|
"shd_restore_note_revision",
|
||||||
|
"shd_restore_organization",
|
||||||
|
"shd_restore_proposal_version",
|
||||||
|
"shd_restore_terms_document_revision",
|
||||||
|
"shd_restore_terms_version",
|
||||||
|
"shd_retry_finance_import",
|
||||||
|
"shd_retry_task_automation_log",
|
||||||
|
"shd_retry_task_form_submission",
|
||||||
|
"shd_retry_task_portfolio_automation_log",
|
||||||
|
"shd_return_inventory_asset",
|
||||||
|
"shd_review_task_timesheet",
|
||||||
|
"shd_revoke_document_public_link",
|
||||||
|
"shd_revoke_note_member",
|
||||||
|
"shd_revoke_proposal_public_link",
|
||||||
|
"shd_revoke_task_form",
|
||||||
|
"shd_run_task_automation_rule",
|
||||||
|
"shd_run_task_portfolio_automation_rule",
|
||||||
|
"shd_save_alice_selected_devices",
|
||||||
|
"shd_save_crm_entity",
|
||||||
|
"shd_save_discussion_topic_draft",
|
||||||
|
"shd_save_document_contract",
|
||||||
|
"shd_save_exchange_rate",
|
||||||
|
"shd_save_finance_account",
|
||||||
|
"shd_save_finance_category",
|
||||||
|
"shd_save_finance_counterparty",
|
||||||
|
"shd_save_finance_document",
|
||||||
|
"shd_save_finance_legal_entity",
|
||||||
|
"shd_save_finance_payment",
|
||||||
|
"shd_save_finance_project",
|
||||||
|
"shd_save_homekit_selected_devices",
|
||||||
|
"shd_save_proposal_template",
|
||||||
|
"shd_save_proposal_template_library_item",
|
||||||
|
"shd_save_scheme_description_template",
|
||||||
|
"shd_save_scheme_point_discussion",
|
||||||
|
"shd_save_scheme_state",
|
||||||
|
"shd_save_task_import_template",
|
||||||
|
"shd_save_voice_agent_config",
|
||||||
|
"shd_schedule_discussion_message",
|
||||||
|
"shd_search",
|
||||||
|
"shd_search_discussions",
|
||||||
|
"shd_search_note_blocks",
|
||||||
|
"shd_search_notes",
|
||||||
|
"shd_send_document",
|
||||||
|
"shd_send_proposal",
|
||||||
|
"shd_set_discussion_message_reminder",
|
||||||
|
"shd_set_note_review_comment_status",
|
||||||
|
"shd_set_project_archived",
|
||||||
|
"shd_set_scheduling_availability_active",
|
||||||
|
"shd_set_scheduling_availability_paused",
|
||||||
|
"shd_set_scheduling_event_type_active",
|
||||||
|
"shd_star_discussion_message",
|
||||||
|
"shd_start_finance_import",
|
||||||
|
"shd_start_realtime_session",
|
||||||
|
"shd_start_task_timer",
|
||||||
|
"shd_stop_task_timer",
|
||||||
|
"shd_stream_file",
|
||||||
|
"shd_submit_task_form",
|
||||||
|
"shd_submit_task_timesheet",
|
||||||
|
"shd_sync_alice_devices",
|
||||||
|
"shd_sync_homekit",
|
||||||
|
"shd_sync_task_portfolio_projects",
|
||||||
|
"shd_sync_voice_agent_config",
|
||||||
|
"shd_system_db_delete_record",
|
||||||
|
"shd_system_db_record",
|
||||||
|
"shd_system_db_records",
|
||||||
|
"shd_system_db_schema",
|
||||||
|
"shd_system_db_update_cell",
|
||||||
|
"shd_task_action",
|
||||||
|
"shd_terminate_realtime_session",
|
||||||
|
"shd_terms_description_comment",
|
||||||
|
"shd_terms_document_action",
|
||||||
|
"shd_terms_point_discussion",
|
||||||
|
"shd_test_controller_connection",
|
||||||
|
"shd_test_voice_agent_rule",
|
||||||
|
"shd_unhide_discussion_channel",
|
||||||
|
"shd_unlink_crm_entities",
|
||||||
|
"shd_unlink_current_user_oidc",
|
||||||
|
"shd_unpin_discussion_message",
|
||||||
|
"shd_update_agent",
|
||||||
|
"shd_update_agent_notifications",
|
||||||
|
"shd_update_controller",
|
||||||
|
"shd_update_crm_activity",
|
||||||
|
"shd_update_crm_lead",
|
||||||
|
"shd_update_crm_note",
|
||||||
|
"shd_update_crm_reminder",
|
||||||
|
"shd_update_current_user",
|
||||||
|
"shd_update_discussion_channel_state",
|
||||||
|
"shd_update_discussion_members",
|
||||||
|
"shd_update_discussion_message",
|
||||||
|
"shd_update_discussion_topic",
|
||||||
|
"shd_update_document_comment",
|
||||||
|
"shd_update_document_template",
|
||||||
|
"shd_update_finance_document_item",
|
||||||
|
"shd_update_finance_public_link_settings",
|
||||||
|
"shd_update_gitea_issue_labels",
|
||||||
|
"shd_update_grafana_settings",
|
||||||
|
"shd_update_inventory_public_link",
|
||||||
|
"shd_update_inventory_resource",
|
||||||
|
"shd_update_note_collection",
|
||||||
|
"shd_update_note_database",
|
||||||
|
"shd_update_note_database_property",
|
||||||
|
"shd_update_note_database_record",
|
||||||
|
"shd_update_note_database_view",
|
||||||
|
"shd_update_note_page",
|
||||||
|
"shd_update_note_space",
|
||||||
|
"shd_update_note_space_presence",
|
||||||
|
"shd_update_notification_preferences",
|
||||||
|
"shd_update_organization",
|
||||||
|
"shd_update_organization_member",
|
||||||
|
"shd_update_organization_two_factor_policy",
|
||||||
|
"shd_update_project",
|
||||||
|
"shd_update_project_device",
|
||||||
|
"shd_update_project_service",
|
||||||
|
"shd_update_project_settings",
|
||||||
|
"shd_update_proposal",
|
||||||
|
"shd_update_proposal_access_settings",
|
||||||
|
"shd_update_proposal_notification_settings",
|
||||||
|
"shd_update_scheduled_discussion_message",
|
||||||
|
"shd_update_scheduling_availability_rule",
|
||||||
|
"shd_update_scheduling_booking",
|
||||||
|
"shd_update_scheduling_event_type",
|
||||||
|
"shd_update_scheduling_settings",
|
||||||
|
"shd_update_scheme_description_comment",
|
||||||
|
"shd_update_status_page_group",
|
||||||
|
"shd_update_status_page_monitor",
|
||||||
|
"shd_update_task",
|
||||||
|
"shd_update_task_attachment_annotation",
|
||||||
|
"shd_update_task_automation_rule",
|
||||||
|
"shd_update_task_checklist_item",
|
||||||
|
"shd_update_task_custom_field",
|
||||||
|
"shd_update_task_form",
|
||||||
|
"shd_update_task_goal",
|
||||||
|
"shd_update_task_portfolio",
|
||||||
|
"shd_update_task_portfolio_automation_rule",
|
||||||
|
"shd_update_task_portfolio_member",
|
||||||
|
"shd_update_task_portfolio_workload_settings",
|
||||||
|
"shd_update_task_project_member",
|
||||||
|
"shd_update_task_project_presence",
|
||||||
|
"shd_update_task_project_settings",
|
||||||
|
"shd_update_task_project_workload_settings",
|
||||||
|
"shd_update_task_sprint",
|
||||||
|
"shd_update_task_status",
|
||||||
|
"shd_update_task_status_definition",
|
||||||
|
"shd_update_task_view",
|
||||||
|
"shd_update_terms_document",
|
||||||
|
"shd_update_terms_point",
|
||||||
|
"shd_update_terms_room",
|
||||||
|
"shd_upload_discussion_attachment",
|
||||||
|
"shd_upload_file",
|
||||||
|
"shd_upload_finance_document_attachment",
|
||||||
|
"shd_upload_gitea_issue_attachment",
|
||||||
|
"shd_upload_note_attachment",
|
||||||
|
"shd_upload_proposal_template_logo",
|
||||||
|
"shd_upload_task_attachment",
|
||||||
|
"shd_validate_terms_document",
|
||||||
|
"shd_write_off_inventory_items",
|
||||||
|
"shd_write_off_inventory_stock"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -33,16 +33,23 @@ modules. Skills do not grant permissions or bypass server-side ACLs.
|
|||||||
- linked Gitea issues, labels, comments and attachments;
|
- linked Gitea issues, labels, comments and attachments;
|
||||||
- safe bulk-change planning and validation rules.
|
- safe bulk-change planning and validation rules.
|
||||||
|
|
||||||
When the connected MCP host supports MCP Apps UI, the active-projects workflow
|
When the connected MCP host supports MCP Apps UI, the 36 SHD render tools have
|
||||||
also has an inline widget: it sorts by the nearest completion date, filters by
|
inline read-only widgets: active projects, module registers and specialized
|
||||||
name/code, shows status pills and loads one project’s read-only details. The
|
Kanban, workload, timeline, funnel, dashboard, calendar, matrix, tree and
|
||||||
same tools still return normal structured data for hosts that do not render
|
Project DB views. Each widget sorts or filters normalized records, shows
|
||||||
|
status/value fields and can load one selected record’s details where supported.
|
||||||
|
The same tools still return normal structured data for hosts that do not render
|
||||||
widgets.
|
widgets.
|
||||||
|
|
||||||
The active-projects widget is versioned in `widgets/active-projects/v1/` with a
|
The active-projects widget is versioned in `widgets/active-projects/v1/` with a
|
||||||
manifest, SHA256 checksum and source/provenance note. The SHD MCP backend reads
|
manifest, SHA256 checksum and source/provenance note. The SHD MCP backend reads
|
||||||
that artifact for the `ui://shd/active-projects/v1.html` resource.
|
that artifact for the `ui://shd/active-projects/v1.html` resource.
|
||||||
|
|
||||||
|
The other versioned resources are stored under `widgets/` with a manifest,
|
||||||
|
SHA256 checksum and source/provenance note for each resource. Their render
|
||||||
|
tools are deliberately separate from the list tools so the server can keep
|
||||||
|
data access and UI rendering auditable.
|
||||||
|
|
||||||
## MCP connection
|
## MCP connection
|
||||||
|
|
||||||
The package points to `https://shd.xyz.su/mcp` and requests OAuth for that MCP
|
The package points to `https://shd.xyz.su/mcp` and requests OAuth for that MCP
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
806f28ac654a7504ae5a258add4c03114dfc9fc80328d479e23b09be92c7a74d index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# ACL matrix widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/acl-matrix/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only organization membership and permission matrix.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="acl-matrix"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='acl-matrix';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-acl-matrix",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48773,
|
||||||
|
"gzipBytes": 13973,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/acl-matrix/v1.html",
|
||||||
|
"sha256": "806f28ac654a7504ae5a258add4c03114dfc9fc80328d479e23b09be92c7a74d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/acl-matrix/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
49b3ab8629540f2c1075c3e096e2990055ec56181e6dd02dcd998bc98d9c7069 index.html
|
f5338c9d833ea57be5823d0f302e00ad8dc85ce5645681e71fdad134d6465ba1 index.html
|
||||||
|
|||||||
@@ -7,40 +7,40 @@
|
|||||||
:root {
|
:root {
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
--bg: #ffffff;
|
--bg: #f8fafc;
|
||||||
--surface: #f6f8fb;
|
--surface: #e8edf2;
|
||||||
--surface-hover: #eef3f9;
|
--surface-hover: #e1e8ef;
|
||||||
--border: #d9e1ec;
|
--border: rgba(31, 41, 55, .16);
|
||||||
--text: #152235;
|
--text: rgb(29, 31, 37);
|
||||||
--muted: #63728a;
|
--muted: rgb(97, 102, 112);
|
||||||
--accent: #1769e0;
|
--accent: rgb(83, 89, 101);
|
||||||
--accent-soft: #e7f0ff;
|
--accent-soft: rgba(75, 85, 99, .12);
|
||||||
--success: #16794b;
|
--success: #16794b;
|
||||||
--success-soft: #e6f7ef;
|
--success-soft: #e6f7ef;
|
||||||
--warning: #9a6100;
|
--warning: #9a6100;
|
||||||
--warning-soft: #fff4d8;
|
--warning-soft: #fff4d8;
|
||||||
--danger: #b42318;
|
--danger: #b42318;
|
||||||
--danger-soft: #ffebe9;
|
--danger-soft: #ffebe9;
|
||||||
--shadow: 0 8px 24px rgba(25, 55, 95, .08);
|
--shadow: 0 0 0 1px rgba(15, 23, 42, .07), 0 8px 22px rgba(15, 23, 42, .11);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--bg: #172131;
|
--bg: #202020;
|
||||||
--surface: #202c3e;
|
--surface: #292929;
|
||||||
--surface-hover: #29384c;
|
--surface-hover: #303336;
|
||||||
--border: #39495f;
|
--border: #43474c;
|
||||||
--text: #eef4fc;
|
--text: #eaecf0;
|
||||||
--muted: #a8b6c9;
|
--muted: #96999d;
|
||||||
--accent: #75a9ff;
|
--accent: #c7b07a;
|
||||||
--accent-soft: #263f68;
|
--accent-soft: rgba(199, 176, 122, .18);
|
||||||
--success: #70d4a4;
|
--success: #70d4a4;
|
||||||
--success-soft: #173d31;
|
--success-soft: #173d31;
|
||||||
--warning: #f4c467;
|
--warning: #f4c467;
|
||||||
--warning-soft: #4b3918;
|
--warning-soft: #4b3918;
|
||||||
--danger: #ff9b91;
|
--danger: #ff9b91;
|
||||||
--danger-soft: #512522;
|
--danger-soft: #512522;
|
||||||
--shadow: 0 8px 24px rgba(0, 0, 0, .22);
|
--shadow: 0 10px 26px rgba(0, 0, 0, .34);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 22732,
|
"bytes": 22838,
|
||||||
"gzipBytes": 6133,
|
"gzipBytes": 6186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/active-projects/v1.html",
|
"uri": "ui://shd/active-projects/v1.html",
|
||||||
"sha256": "49b3ab8629540f2c1075c3e096e2990055ec56181e6dd02dcd998bc98d9c7069"
|
"sha256": "f5338c9d833ea57be5823d0f302e00ad8dc85ce5645681e71fdad134d6465ba1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
3af72830f40d1cb3e10194f875c6e403bf5f112151399e623c858981dfcaab5b index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Activity widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/activity/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Authenticated realtime activity stream.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='activity';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){state.config.detailSourceArgs.forEach(function(name){if(state.config.sourceArgs&&state.config.sourceArgs[name]!==undefined)a[name]=state.config.sourceArgs[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code&&!a.project){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-activity",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 25600,
|
||||||
|
"gzipBytes": 8709,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/activity/v1.html",
|
||||||
|
"sha256": "3af72830f40d1cb3e10194f875c6e403bf5f112151399e623c858981dfcaab5b"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/activity/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2795c7d06cbcc95984f34ef5f0c802b4653ec4af9239d07337ba20e795017bc0 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Agents health widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/agents-health/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only agent and controller health overview.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="agents-health"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='agents-health';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-agents-health",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48779,
|
||||||
|
"gzipBytes": 13974,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/agents-health/v1.html",
|
||||||
|
"sha256": "2795c7d06cbcc95984f34ef5f0c802b4653ec4af9239d07337ba20e795017bc0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/agents-health/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
f1576f8f4653c7178b17545c136eb3609099ebd4c581ddbff5fe52331a7b71f0 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Agents widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/agents/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides agents and controllers with read-only status context.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="agents"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='agents';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-agents",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21061,
|
||||||
|
"gzipBytes": 7411,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/agents/v1.html",
|
||||||
|
"sha256": "f1576f8f4653c7178b17545c136eb3609099ebd4c581ddbff5fe52331a7b71f0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/agents/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8c7aab8e966bcb14f48ba6a24993654b5083c0b85f9898aec6d4d478c56b0c22 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# CRM funnel widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/crm-funnel/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only CRM deal funnel grouped by stage.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="crm-funnel"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='crm-funnel';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-crm-funnel",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48773,
|
||||||
|
"gzipBytes": 13974,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/crm-funnel/v1.html",
|
||||||
|
"sha256": "8c7aab8e966bcb14f48ba6a24993654b5083c0b85f9898aec6d4d478c56b0c22"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/crm-funnel/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
64f552a54e178e55d0102a050d001751051ae4d2c50370dbdd24201263499603 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# CRM widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/crm/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>CRM — SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f7f8fa;--panel:#fff;--line:#d9dee7;--ink:#172033;--muted:#687386;--accent:#2563eb;--accent-soft:#e8efff;--danger:#b42318}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
||||||
|
.shell{max-width:980px;margin:0 auto;padding:24px}.eyebrow{color:var(--accent);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}
|
||||||
|
h1{margin:4px 0 6px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0 14px}
|
||||||
|
.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--panel);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:12px}.search{min-width:0;flex:1;padding:9px 11px;border:1px solid var(--line);border-radius:8px;background:var(--panel);color:inherit}
|
||||||
|
button{cursor:pointer}.refresh{padding:9px 14px;border:1px solid var(--accent);border-radius:8px;background:var(--accent);color:#fff;font-weight:600}.refresh:disabled{opacity:.6;cursor:wait}
|
||||||
|
.status-message{min-height:22px;color:var(--muted)}.error{color:var(--danger)}.table-wrap{overflow:auto;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
|
||||||
|
table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{color:var(--muted);font-size:12px;font-weight:600}
|
||||||
|
tbody tr:last-child td{border-bottom:0}.record{width:100%;border:0;background:transparent;color:inherit;text-align:left}.record:hover{background:var(--accent-soft)}
|
||||||
|
.title{font-weight:650}.subtitle{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:12px;white-space:nowrap}
|
||||||
|
.empty{padding:24px;text-align:center;color:var(--muted)}.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:10px;background:var(--panel)}.details[hidden]{display:none}
|
||||||
|
.details-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}
|
||||||
|
.detail{padding:9px;border-radius:8px;background:var(--bg)}.detail-label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.detail-value{overflow-wrap:anywhere}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#11151d;--panel:#1b2230;--line:#303a4b;--ink:#edf2fb;--muted:#aab4c5;--accent:#82aaff;--accent-soft:#263858;--danger:#ff9a93}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="crm">
|
||||||
|
<header>
|
||||||
|
<div class="eyebrow">SHD MCP</div>
|
||||||
|
<h1>CRM</h1>
|
||||||
|
<p class="lede">Реестр CRM-сущностей с поиском и просмотром деталей.</p>
|
||||||
|
<div class="summary" id="summary"></div>
|
||||||
|
</header>
|
||||||
|
<div class="toolbar">
|
||||||
|
<input class="search" id="search" type="search" placeholder="Поиск по названию, статусу или ID" autocomplete="off">
|
||||||
|
<button class="refresh" id="refresh" type="button">Обновить</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-message" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Сущность</th><th>Статус</th><th>Обновлено</th><th>Тип</th></tr></thead>
|
||||||
|
<tbody id="rows"></tbody>
|
||||||
|
</table>
|
||||||
|
<div class="empty" id="empty" hidden>Нет данных для отображения.</div>
|
||||||
|
</div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite">
|
||||||
|
<div class="details-head"><div><h2 id="details-title"></h2><div class="subtitle" id="details-subtitle"></div></div><button class="close" id="close" type="button" aria-label="Закрыть">×</button></div>
|
||||||
|
<div class="detail-grid" id="detail-grid"></div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var CONFIG = {"module":"crm","title":"CRM","description":"Реестр CRM-сущностей с поиском и просмотром деталей.","nameLabel":"Сущность","statusLabel":"Статус","dateLabel":"Обновлено","valueLabel":"Тип","listTool":"shd_list_crm","detailTool":"shd_get_crm_entity","detailKey":"entity_id","detailSection":"deals","listArgs":{"section":"deals","archived":false,"limit":100}};
|
||||||
|
var pending = new Map();
|
||||||
|
var nextRequestId = 1;
|
||||||
|
var records = [];
|
||||||
|
var busy = false;
|
||||||
|
var elements = {
|
||||||
|
search: document.getElementById('search'),
|
||||||
|
refresh: document.getElementById('refresh'),
|
||||||
|
status: document.getElementById('status'),
|
||||||
|
summary: document.getElementById('summary'),
|
||||||
|
rows: document.getElementById('rows'),
|
||||||
|
empty: document.getElementById('empty'),
|
||||||
|
details: document.getElementById('details'),
|
||||||
|
detailsTitle: document.getElementById('details-title'),
|
||||||
|
detailsSubtitle: document.getElementById('details-subtitle'),
|
||||||
|
detailGrid: document.getElementById('detail-grid'),
|
||||||
|
close: document.getElementById('close')
|
||||||
|
};
|
||||||
|
|
||||||
|
function request(method, params) {
|
||||||
|
var openai = typeof window !== 'undefined' ? window.openai : undefined;
|
||||||
|
if (method === 'tools/call' && openai && typeof openai.callTool === 'function') {
|
||||||
|
return openai.callTool(params.name, params.arguments || {});
|
||||||
|
}
|
||||||
|
var id = nextRequestId++;
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var timeout = window.setTimeout(function () {
|
||||||
|
pending.delete(id);
|
||||||
|
reject(new Error('Host request timed out'));
|
||||||
|
}, 10000);
|
||||||
|
pending.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(method, params) {
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params || {} }, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePayload(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.structuredContent) return value.structuredContent;
|
||||||
|
if (value.toolOutput) return value.toolOutput;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (value && value.isError) throw new Error('MCP tool returned an error');
|
||||||
|
if (payload && payload.error) throw new Error(String(payload.error));
|
||||||
|
return payload || value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toArray(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (!payload) return [];
|
||||||
|
var data = payload.data;
|
||||||
|
if (data && !Array.isArray(data) && Array.isArray(data.data)) data = data.data;
|
||||||
|
if (Array.isArray(payload.items)) data = payload.items;
|
||||||
|
return Array.isArray(data) ? data.filter(function (item) { return item && typeof item === 'object'; }) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function idOf(item) { return String(item.id || item.code || item.key || item.number || ''); }
|
||||||
|
function titleOf(item) { return String(item.title || item.name || item.display_name || item.number || idOf(item) || '—'); }
|
||||||
|
function subtitleOf(item) { return String(item.subtitle || item.project_code || item.code || item.key || ''); }
|
||||||
|
function statusOf(item) {
|
||||||
|
var value = item.status;
|
||||||
|
if (value && typeof value === 'object') value = value.name || value.id;
|
||||||
|
return String(value || item.phase || '—');
|
||||||
|
}
|
||||||
|
function dateOf(item) { return String(item.date || item.due_date || item.updated_at || item.issued_at || item.created_at || ''); }
|
||||||
|
function valueOf(item) { return item.value === null || item.value === undefined ? '' : String(item.value); }
|
||||||
|
function dateText(value) {
|
||||||
|
if (!value) return '—';
|
||||||
|
var timestamp = Date.parse(value);
|
||||||
|
if (Number.isNaN(timestamp)) return value;
|
||||||
|
try { return new Intl.DateTimeFormat(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(new Date(timestamp)); }
|
||||||
|
catch (error) { return value; }
|
||||||
|
}
|
||||||
|
function make(tag, value, className) {
|
||||||
|
var node = document.createElement(tag);
|
||||||
|
if (className) node.className = className;
|
||||||
|
if (value !== undefined) node.textContent = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSummary() {
|
||||||
|
elements.summary.replaceChildren();
|
||||||
|
var item = make('span');
|
||||||
|
item.append(make('strong', String(records.length)), ' записей');
|
||||||
|
elements.summary.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRows() {
|
||||||
|
var query = elements.search.value.trim().toLowerCase();
|
||||||
|
var filtered = records.filter(function (item) {
|
||||||
|
if (!query) return true;
|
||||||
|
return (titleOf(item) + ' ' + subtitleOf(item) + ' ' + statusOf(item) + ' ' + idOf(item)).toLowerCase().indexOf(query) !== -1;
|
||||||
|
});
|
||||||
|
elements.rows.replaceChildren();
|
||||||
|
elements.empty.hidden = filtered.length !== 0;
|
||||||
|
filtered.forEach(function (item) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
var name = make('td');
|
||||||
|
name.append(make('span', titleOf(item), 'title'));
|
||||||
|
if (subtitleOf(item)) name.append(make('span', subtitleOf(item), 'subtitle'));
|
||||||
|
var status = make('td');
|
||||||
|
status.append(make('span', statusOf(item), 'pill'));
|
||||||
|
row.append(name, status, make('td', dateText(dateOf(item))), make('td', valueOf(item) || '—'));
|
||||||
|
row.className = 'record';
|
||||||
|
row.tabIndex = 0;
|
||||||
|
row.setAttribute('role', 'button');
|
||||||
|
row.setAttribute('aria-label', titleOf(item));
|
||||||
|
row.addEventListener('click', function () { showDetails(item); });
|
||||||
|
row.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') showDetails(item); });
|
||||||
|
elements.rows.append(row);
|
||||||
|
});
|
||||||
|
renderSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(value) {
|
||||||
|
records = toArray(value).slice().sort(function (left, right) {
|
||||||
|
var a = Date.parse(dateOf(left)); var b = Date.parse(dateOf(right));
|
||||||
|
if (Number.isNaN(a)) a = Number.POSITIVE_INFINITY;
|
||||||
|
if (Number.isNaN(b)) b = Number.POSITIVE_INFINITY;
|
||||||
|
return a - b || titleOf(left).localeCompare(titleOf(right));
|
||||||
|
});
|
||||||
|
renderRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(value, isError) {
|
||||||
|
elements.status.textContent = value || '';
|
||||||
|
elements.status.className = isError ? 'status-message error' : 'status-message';
|
||||||
|
}
|
||||||
|
|
||||||
|
function detailValue(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '—';
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
try { return JSON.stringify(value); } catch (error) { return '—'; }
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDetail(label, value) {
|
||||||
|
var item = make('div', undefined, 'detail');
|
||||||
|
item.append(make('span', label, 'detail-label'), make('span', detailValue(value), 'detail-value'));
|
||||||
|
elements.detailGrid.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetails(item) {
|
||||||
|
elements.detailsTitle.textContent = titleOf(item);
|
||||||
|
elements.detailsSubtitle.textContent = subtitleOf(item) || idOf(item);
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
addDetail('ID', idOf(item));
|
||||||
|
addDetail(CONFIG.statusLabel, statusOf(item));
|
||||||
|
addDetail(CONFIG.dateLabel, dateText(dateOf(item)));
|
||||||
|
if (valueOf(item)) addDetail(CONFIG.valueLabel, valueOf(item));
|
||||||
|
if (item.meta) addDetail('Meta', item.meta);
|
||||||
|
elements.details.hidden = false;
|
||||||
|
var id = idOf(item);
|
||||||
|
if (!id || !CONFIG.detailTool) return;
|
||||||
|
var args = {};
|
||||||
|
args[CONFIG.detailKey] = Number(id) || id;
|
||||||
|
if (CONFIG.detailSection) args.section = CONFIG.detailSection;
|
||||||
|
request('tools/call', { name: CONFIG.detailTool, arguments: args }).then(function (result) {
|
||||||
|
var payload = unwrap(result);
|
||||||
|
var data = payload && payload.data;
|
||||||
|
if (Array.isArray(data)) data = data[0];
|
||||||
|
if (!data || typeof data !== 'object') return;
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
Object.keys(data).slice(0, 12).forEach(function (key) { addDetail(key, data[key]); });
|
||||||
|
}).catch(function (error) { setStatus(error && error.message ? error.message : 'Не удалось загрузить детали.', true); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (busy) return;
|
||||||
|
busy = true;
|
||||||
|
elements.refresh.disabled = true;
|
||||||
|
setStatus('Загрузка…', false);
|
||||||
|
request('tools/call', { name: CONFIG.listTool, arguments: CONFIG.listArgs }).then(function (result) {
|
||||||
|
render(unwrap(result));
|
||||||
|
setStatus('', false);
|
||||||
|
}).catch(function (error) {
|
||||||
|
setStatus(error && error.message ? error.message : 'Не удалось загрузить данные.', true);
|
||||||
|
}).finally(function () {
|
||||||
|
busy = false;
|
||||||
|
elements.refresh.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeHost() {
|
||||||
|
return request('ui/initialize', {
|
||||||
|
protocolVersion: '2025-11-25',
|
||||||
|
appInfo: { name: 'SHD ' + CONFIG.title, title: 'SHD ' + CONFIG.title, version: '1.0.0', websiteUrl: 'https://shd.xyz.su' },
|
||||||
|
appCapabilities: {}
|
||||||
|
}).then(function () { notify('ui/notifications/initialized', {}); }).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
var message = event.data;
|
||||||
|
if (!message || message.jsonrpc !== '2.0') return;
|
||||||
|
if (message.id !== undefined && pending.has(message.id)) {
|
||||||
|
var entry = pending.get(message.id);
|
||||||
|
pending.delete(message.id);
|
||||||
|
window.clearTimeout(entry.timeout);
|
||||||
|
if (message.error) entry.reject(new Error(message.error.message || 'Host request failed'));
|
||||||
|
else entry.resolve(message.result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-input') {
|
||||||
|
if (!records.length) render(message.params || {});
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-result') render(message.params && message.params.structuredContent);
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
elements.search.addEventListener('input', renderRows);
|
||||||
|
elements.refresh.addEventListener('click', refresh);
|
||||||
|
elements.close.addEventListener('click', function () { elements.details.hidden = true; });
|
||||||
|
void initializeHost();
|
||||||
|
var initialOutput = typeof window !== 'undefined' && window.openai ? window.openai.toolOutput : null;
|
||||||
|
if (initialOutput) render(initialOutput);
|
||||||
|
else {
|
||||||
|
var initialInput = typeof window !== 'undefined' && window.openai ? window.openai.toolInput : null;
|
||||||
|
if (initialInput) render(initialInput);
|
||||||
|
else refresh();
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-crm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"uri": "ui://shd/crm/v1.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"bytes": 15485,
|
||||||
|
"sha256": "64f552a54e178e55d0102a050d001751051ae4d2c50370dbdd24201263499603"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/crm/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
c9ae4958b7c0f9dc1e872811e310809b51e614ace2850e8558d6c240e612e3c0 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Discussions widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/discussions/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides topics and read-only message details.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="discussions"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='discussions';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-discussions",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21071,
|
||||||
|
"gzipBytes": 7411,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/discussions/v1.html",
|
||||||
|
"sha256": "c9ae4958b7c0f9dc1e872811e310809b51e614ace2850e8558d6c240e612e3c0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/discussions/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
553db1686496a3fa6e2097924cb33db72ca24f0a68a4a8454cad2e14ad0ea960 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Documents completeness widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/documents-completeness/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Terms and document completeness dashboard.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="documents-completeness"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='documents-completeness';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-documents-completeness",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48797,
|
||||||
|
"gzipBytes": 13975,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/documents-completeness/v1.html",
|
||||||
|
"sha256": "553db1686496a3fa6e2097924cb33db72ca24f0a68a4a8454cad2e14ad0ea960"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/documents-completeness/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
5959d58c47ab81813202df203cf69a568a646d61884e64802e113e752229d925 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Документы widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/documents/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Документы — SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f7f8fa;--panel:#fff;--line:#d9dee7;--ink:#172033;--muted:#687386;--accent:#2563eb;--accent-soft:#e8efff;--danger:#b42318}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
||||||
|
.shell{max-width:980px;margin:0 auto;padding:24px}.eyebrow{color:var(--accent);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}
|
||||||
|
h1{margin:4px 0 6px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0 14px}
|
||||||
|
.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--panel);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:12px}.search{min-width:0;flex:1;padding:9px 11px;border:1px solid var(--line);border-radius:8px;background:var(--panel);color:inherit}
|
||||||
|
button{cursor:pointer}.refresh{padding:9px 14px;border:1px solid var(--accent);border-radius:8px;background:var(--accent);color:#fff;font-weight:600}.refresh:disabled{opacity:.6;cursor:wait}
|
||||||
|
.status-message{min-height:22px;color:var(--muted)}.error{color:var(--danger)}.table-wrap{overflow:auto;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
|
||||||
|
table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{color:var(--muted);font-size:12px;font-weight:600}
|
||||||
|
tbody tr:last-child td{border-bottom:0}.record{width:100%;border:0;background:transparent;color:inherit;text-align:left}.record:hover{background:var(--accent-soft)}
|
||||||
|
.title{font-weight:650}.subtitle{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:12px;white-space:nowrap}
|
||||||
|
.empty{padding:24px;text-align:center;color:var(--muted)}.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:10px;background:var(--panel)}.details[hidden]{display:none}
|
||||||
|
.details-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}
|
||||||
|
.detail{padding:9px;border-radius:8px;background:var(--bg)}.detail-label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.detail-value{overflow-wrap:anywhere}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#11151d;--panel:#1b2230;--line:#303a4b;--ink:#edf2fb;--muted:#aab4c5;--accent:#82aaff;--accent-soft:#263858;--danger:#ff9a93}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="documents">
|
||||||
|
<header>
|
||||||
|
<div class="eyebrow">SHD MCP</div>
|
||||||
|
<h1>Документы</h1>
|
||||||
|
<p class="lede">Реестр документов с поиском и просмотром деталей.</p>
|
||||||
|
<div class="summary" id="summary"></div>
|
||||||
|
</header>
|
||||||
|
<div class="toolbar">
|
||||||
|
<input class="search" id="search" type="search" placeholder="Поиск по названию, статусу или ID" autocomplete="off">
|
||||||
|
<button class="refresh" id="refresh" type="button">Обновить</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-message" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Документ</th><th>Статус</th><th>Обновлено</th><th>Сумма</th></tr></thead>
|
||||||
|
<tbody id="rows"></tbody>
|
||||||
|
</table>
|
||||||
|
<div class="empty" id="empty" hidden>Нет данных для отображения.</div>
|
||||||
|
</div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite">
|
||||||
|
<div class="details-head"><div><h2 id="details-title"></h2><div class="subtitle" id="details-subtitle"></div></div><button class="close" id="close" type="button" aria-label="Закрыть">×</button></div>
|
||||||
|
<div class="detail-grid" id="detail-grid"></div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var CONFIG = {"module":"documents","title":"Документы","description":"Реестр документов с поиском и просмотром деталей.","nameLabel":"Документ","statusLabel":"Статус","dateLabel":"Обновлено","valueLabel":"Сумма","listTool":"shd_list_contracts","detailTool":"shd_get_contract","detailKey":"contract_id","listArgs":{"archived":false,"limit":100}};
|
||||||
|
var pending = new Map();
|
||||||
|
var nextRequestId = 1;
|
||||||
|
var records = [];
|
||||||
|
var busy = false;
|
||||||
|
var elements = {
|
||||||
|
search: document.getElementById('search'),
|
||||||
|
refresh: document.getElementById('refresh'),
|
||||||
|
status: document.getElementById('status'),
|
||||||
|
summary: document.getElementById('summary'),
|
||||||
|
rows: document.getElementById('rows'),
|
||||||
|
empty: document.getElementById('empty'),
|
||||||
|
details: document.getElementById('details'),
|
||||||
|
detailsTitle: document.getElementById('details-title'),
|
||||||
|
detailsSubtitle: document.getElementById('details-subtitle'),
|
||||||
|
detailGrid: document.getElementById('detail-grid'),
|
||||||
|
close: document.getElementById('close')
|
||||||
|
};
|
||||||
|
|
||||||
|
function request(method, params) {
|
||||||
|
var openai = typeof window !== 'undefined' ? window.openai : undefined;
|
||||||
|
if (method === 'tools/call' && openai && typeof openai.callTool === 'function') {
|
||||||
|
return openai.callTool(params.name, params.arguments || {});
|
||||||
|
}
|
||||||
|
var id = nextRequestId++;
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var timeout = window.setTimeout(function () {
|
||||||
|
pending.delete(id);
|
||||||
|
reject(new Error('Host request timed out'));
|
||||||
|
}, 10000);
|
||||||
|
pending.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(method, params) {
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params || {} }, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePayload(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.structuredContent) return value.structuredContent;
|
||||||
|
if (value.toolOutput) return value.toolOutput;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (value && value.isError) throw new Error('MCP tool returned an error');
|
||||||
|
if (payload && payload.error) throw new Error(String(payload.error));
|
||||||
|
return payload || value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toArray(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (!payload) return [];
|
||||||
|
var data = payload.data;
|
||||||
|
if (data && !Array.isArray(data) && Array.isArray(data.data)) data = data.data;
|
||||||
|
if (Array.isArray(payload.items)) data = payload.items;
|
||||||
|
return Array.isArray(data) ? data.filter(function (item) { return item && typeof item === 'object'; }) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function idOf(item) { return String(item.id || item.code || item.key || item.number || ''); }
|
||||||
|
function titleOf(item) { return String(item.title || item.name || item.display_name || item.number || idOf(item) || '—'); }
|
||||||
|
function subtitleOf(item) { return String(item.subtitle || item.project_code || item.code || item.key || ''); }
|
||||||
|
function statusOf(item) {
|
||||||
|
var value = item.status;
|
||||||
|
if (value && typeof value === 'object') value = value.name || value.id;
|
||||||
|
return String(value || item.phase || '—');
|
||||||
|
}
|
||||||
|
function dateOf(item) { return String(item.date || item.due_date || item.updated_at || item.issued_at || item.created_at || ''); }
|
||||||
|
function valueOf(item) { return item.value === null || item.value === undefined ? '' : String(item.value); }
|
||||||
|
function dateText(value) {
|
||||||
|
if (!value) return '—';
|
||||||
|
var timestamp = Date.parse(value);
|
||||||
|
if (Number.isNaN(timestamp)) return value;
|
||||||
|
try { return new Intl.DateTimeFormat(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(new Date(timestamp)); }
|
||||||
|
catch (error) { return value; }
|
||||||
|
}
|
||||||
|
function make(tag, value, className) {
|
||||||
|
var node = document.createElement(tag);
|
||||||
|
if (className) node.className = className;
|
||||||
|
if (value !== undefined) node.textContent = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSummary() {
|
||||||
|
elements.summary.replaceChildren();
|
||||||
|
var item = make('span');
|
||||||
|
item.append(make('strong', String(records.length)), ' записей');
|
||||||
|
elements.summary.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRows() {
|
||||||
|
var query = elements.search.value.trim().toLowerCase();
|
||||||
|
var filtered = records.filter(function (item) {
|
||||||
|
if (!query) return true;
|
||||||
|
return (titleOf(item) + ' ' + subtitleOf(item) + ' ' + statusOf(item) + ' ' + idOf(item)).toLowerCase().indexOf(query) !== -1;
|
||||||
|
});
|
||||||
|
elements.rows.replaceChildren();
|
||||||
|
elements.empty.hidden = filtered.length !== 0;
|
||||||
|
filtered.forEach(function (item) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
var name = make('td');
|
||||||
|
name.append(make('span', titleOf(item), 'title'));
|
||||||
|
if (subtitleOf(item)) name.append(make('span', subtitleOf(item), 'subtitle'));
|
||||||
|
var status = make('td');
|
||||||
|
status.append(make('span', statusOf(item), 'pill'));
|
||||||
|
row.append(name, status, make('td', dateText(dateOf(item))), make('td', valueOf(item) || '—'));
|
||||||
|
row.className = 'record';
|
||||||
|
row.tabIndex = 0;
|
||||||
|
row.setAttribute('role', 'button');
|
||||||
|
row.setAttribute('aria-label', titleOf(item));
|
||||||
|
row.addEventListener('click', function () { showDetails(item); });
|
||||||
|
row.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') showDetails(item); });
|
||||||
|
elements.rows.append(row);
|
||||||
|
});
|
||||||
|
renderSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(value) {
|
||||||
|
records = toArray(value).slice().sort(function (left, right) {
|
||||||
|
var a = Date.parse(dateOf(left)); var b = Date.parse(dateOf(right));
|
||||||
|
if (Number.isNaN(a)) a = Number.POSITIVE_INFINITY;
|
||||||
|
if (Number.isNaN(b)) b = Number.POSITIVE_INFINITY;
|
||||||
|
return a - b || titleOf(left).localeCompare(titleOf(right));
|
||||||
|
});
|
||||||
|
renderRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(value, isError) {
|
||||||
|
elements.status.textContent = value || '';
|
||||||
|
elements.status.className = isError ? 'status-message error' : 'status-message';
|
||||||
|
}
|
||||||
|
|
||||||
|
function detailValue(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '—';
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
try { return JSON.stringify(value); } catch (error) { return '—'; }
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDetail(label, value) {
|
||||||
|
var item = make('div', undefined, 'detail');
|
||||||
|
item.append(make('span', label, 'detail-label'), make('span', detailValue(value), 'detail-value'));
|
||||||
|
elements.detailGrid.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetails(item) {
|
||||||
|
elements.detailsTitle.textContent = titleOf(item);
|
||||||
|
elements.detailsSubtitle.textContent = subtitleOf(item) || idOf(item);
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
addDetail('ID', idOf(item));
|
||||||
|
addDetail(CONFIG.statusLabel, statusOf(item));
|
||||||
|
addDetail(CONFIG.dateLabel, dateText(dateOf(item)));
|
||||||
|
if (valueOf(item)) addDetail(CONFIG.valueLabel, valueOf(item));
|
||||||
|
if (item.meta) addDetail('Meta', item.meta);
|
||||||
|
elements.details.hidden = false;
|
||||||
|
var id = idOf(item);
|
||||||
|
if (!id || !CONFIG.detailTool) return;
|
||||||
|
var args = {};
|
||||||
|
args[CONFIG.detailKey] = Number(id) || id;
|
||||||
|
if (CONFIG.detailSection) args.section = CONFIG.detailSection;
|
||||||
|
request('tools/call', { name: CONFIG.detailTool, arguments: args }).then(function (result) {
|
||||||
|
var payload = unwrap(result);
|
||||||
|
var data = payload && payload.data;
|
||||||
|
if (Array.isArray(data)) data = data[0];
|
||||||
|
if (!data || typeof data !== 'object') return;
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
Object.keys(data).slice(0, 12).forEach(function (key) { addDetail(key, data[key]); });
|
||||||
|
}).catch(function (error) { setStatus(error && error.message ? error.message : 'Не удалось загрузить детали.', true); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (busy) return;
|
||||||
|
busy = true;
|
||||||
|
elements.refresh.disabled = true;
|
||||||
|
setStatus('Загрузка…', false);
|
||||||
|
request('tools/call', { name: CONFIG.listTool, arguments: CONFIG.listArgs }).then(function (result) {
|
||||||
|
render(unwrap(result));
|
||||||
|
setStatus('', false);
|
||||||
|
}).catch(function (error) {
|
||||||
|
setStatus(error && error.message ? error.message : 'Не удалось загрузить данные.', true);
|
||||||
|
}).finally(function () {
|
||||||
|
busy = false;
|
||||||
|
elements.refresh.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeHost() {
|
||||||
|
return request('ui/initialize', {
|
||||||
|
protocolVersion: '2025-11-25',
|
||||||
|
appInfo: { name: 'SHD ' + CONFIG.title, title: 'SHD ' + CONFIG.title, version: '1.0.0', websiteUrl: 'https://shd.xyz.su' },
|
||||||
|
appCapabilities: {}
|
||||||
|
}).then(function () { notify('ui/notifications/initialized', {}); }).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
var message = event.data;
|
||||||
|
if (!message || message.jsonrpc !== '2.0') return;
|
||||||
|
if (message.id !== undefined && pending.has(message.id)) {
|
||||||
|
var entry = pending.get(message.id);
|
||||||
|
pending.delete(message.id);
|
||||||
|
window.clearTimeout(entry.timeout);
|
||||||
|
if (message.error) entry.reject(new Error(message.error.message || 'Host request failed'));
|
||||||
|
else entry.resolve(message.result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-input') {
|
||||||
|
if (!records.length) render(message.params || {});
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-result') render(message.params && message.params.structuredContent);
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
elements.search.addEventListener('input', renderRows);
|
||||||
|
elements.refresh.addEventListener('click', refresh);
|
||||||
|
elements.close.addEventListener('click', function () { elements.details.hidden = true; });
|
||||||
|
void initializeHost();
|
||||||
|
var initialOutput = typeof window !== 'undefined' && window.openai ? window.openai.toolOutput : null;
|
||||||
|
if (initialOutput) render(initialOutput);
|
||||||
|
else {
|
||||||
|
var initialInput = typeof window !== 'undefined' && window.openai ? window.openai.toolInput : null;
|
||||||
|
if (initialInput) render(initialInput);
|
||||||
|
else refresh();
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-documents",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"uri": "ui://shd/documents/v1.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"bytes": 15510,
|
||||||
|
"sha256": "5959d58c47ab81813202df203cf69a568a646d61884e64802e113e752229d925"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/documents/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
09c6eed7f36baf0b214e1c9252de4f239405ae86673d06c31f6b5f66913f4144 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Files widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/files/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Project file register with safe read-only links.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='files';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){state.config.detailSourceArgs.forEach(function(name){if(state.config.sourceArgs&&state.config.sourceArgs[name]!==undefined)a[name]=state.config.sourceArgs[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code&&!a.project){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-files",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 25597,
|
||||||
|
"gzipBytes": 8708,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/files/v1.html",
|
||||||
|
"sha256": "09c6eed7f36baf0b214e1c9252de4f239405ae86673d06c31f6b5f66913f4144"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/files/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
7e849281109ad6b9015673083b76ce73fe72b0409eded0ab381b4e48a52170b1 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Finance dashboard widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/finance-dashboard/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only finance totals, statuses and records.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="finance-dashboard"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='finance-dashboard';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-finance-dashboard",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48787,
|
||||||
|
"gzipBytes": 13972,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/finance-dashboard/v1.html",
|
||||||
|
"sha256": "7e849281109ad6b9015673083b76ce73fe72b0409eded0ab381b4e48a52170b1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/finance-dashboard/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
86a8d3275d1fdfaa6bbf5d1ab308b3e19a0b153897f614edd2c23d260030094e index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Финансы widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/finance/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Финансы — SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f7f8fa;--panel:#fff;--line:#d9dee7;--ink:#172033;--muted:#687386;--accent:#2563eb;--accent-soft:#e8efff;--danger:#b42318}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
||||||
|
.shell{max-width:980px;margin:0 auto;padding:24px}.eyebrow{color:var(--accent);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}
|
||||||
|
h1{margin:4px 0 6px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0 14px}
|
||||||
|
.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--panel);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:12px}.search{min-width:0;flex:1;padding:9px 11px;border:1px solid var(--line);border-radius:8px;background:var(--panel);color:inherit}
|
||||||
|
button{cursor:pointer}.refresh{padding:9px 14px;border:1px solid var(--accent);border-radius:8px;background:var(--accent);color:#fff;font-weight:600}.refresh:disabled{opacity:.6;cursor:wait}
|
||||||
|
.status-message{min-height:22px;color:var(--muted)}.error{color:var(--danger)}.table-wrap{overflow:auto;border:1px solid var(--line);border-radius:10px;background:var(--panel)}
|
||||||
|
table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{color:var(--muted);font-size:12px;font-weight:600}
|
||||||
|
tbody tr:last-child td{border-bottom:0}.record{width:100%;border:0;background:transparent;color:inherit;text-align:left}.record:hover{background:var(--accent-soft)}
|
||||||
|
.title{font-weight:650}.subtitle{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:12px;white-space:nowrap}
|
||||||
|
.empty{padding:24px;text-align:center;color:var(--muted)}.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:10px;background:var(--panel)}.details[hidden]{display:none}
|
||||||
|
.details-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}
|
||||||
|
.detail{padding:9px;border-radius:8px;background:var(--bg)}.detail-label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.detail-value{overflow-wrap:anywhere}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#11151d;--panel:#1b2230;--line:#303a4b;--ink:#edf2fb;--muted:#aab4c5;--accent:#82aaff;--accent-soft:#263858;--danger:#ff9a93}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="finance">
|
||||||
|
<header>
|
||||||
|
<div class="eyebrow">SHD MCP</div>
|
||||||
|
<h1>Финансы</h1>
|
||||||
|
<p class="lede">Реестр финансовых документов с поиском и просмотром деталей.</p>
|
||||||
|
<div class="summary" id="summary"></div>
|
||||||
|
</header>
|
||||||
|
<div class="toolbar">
|
||||||
|
<input class="search" id="search" type="search" placeholder="Поиск по названию, статусу или ID" autocomplete="off">
|
||||||
|
<button class="refresh" id="refresh" type="button">Обновить</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-message" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Документ</th><th>Статус</th><th>Срок</th><th>Сумма</th></tr></thead>
|
||||||
|
<tbody id="rows"></tbody>
|
||||||
|
</table>
|
||||||
|
<div class="empty" id="empty" hidden>Нет данных для отображения.</div>
|
||||||
|
</div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite">
|
||||||
|
<div class="details-head"><div><h2 id="details-title"></h2><div class="subtitle" id="details-subtitle"></div></div><button class="close" id="close" type="button" aria-label="Закрыть">×</button></div>
|
||||||
|
<div class="detail-grid" id="detail-grid"></div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var CONFIG = {"module":"finance","title":"Финансы","description":"Реестр финансовых документов с поиском и просмотром деталей.","nameLabel":"Документ","statusLabel":"Статус","dateLabel":"Срок","valueLabel":"Сумма","listTool":"shd_list_finance_documents","detailTool":"shd_get_finance_document","detailKey":"document_id","listArgs":{"archived":false,"limit":100}};
|
||||||
|
var pending = new Map();
|
||||||
|
var nextRequestId = 1;
|
||||||
|
var records = [];
|
||||||
|
var busy = false;
|
||||||
|
var elements = {
|
||||||
|
search: document.getElementById('search'),
|
||||||
|
refresh: document.getElementById('refresh'),
|
||||||
|
status: document.getElementById('status'),
|
||||||
|
summary: document.getElementById('summary'),
|
||||||
|
rows: document.getElementById('rows'),
|
||||||
|
empty: document.getElementById('empty'),
|
||||||
|
details: document.getElementById('details'),
|
||||||
|
detailsTitle: document.getElementById('details-title'),
|
||||||
|
detailsSubtitle: document.getElementById('details-subtitle'),
|
||||||
|
detailGrid: document.getElementById('detail-grid'),
|
||||||
|
close: document.getElementById('close')
|
||||||
|
};
|
||||||
|
|
||||||
|
function request(method, params) {
|
||||||
|
var openai = typeof window !== 'undefined' ? window.openai : undefined;
|
||||||
|
if (method === 'tools/call' && openai && typeof openai.callTool === 'function') {
|
||||||
|
return openai.callTool(params.name, params.arguments || {});
|
||||||
|
}
|
||||||
|
var id = nextRequestId++;
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var timeout = window.setTimeout(function () {
|
||||||
|
pending.delete(id);
|
||||||
|
reject(new Error('Host request timed out'));
|
||||||
|
}, 10000);
|
||||||
|
pending.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(method, params) {
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params || {} }, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePayload(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.structuredContent) return value.structuredContent;
|
||||||
|
if (value.toolOutput) return value.toolOutput;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (value && value.isError) throw new Error('MCP tool returned an error');
|
||||||
|
if (payload && payload.error) throw new Error(String(payload.error));
|
||||||
|
return payload || value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toArray(value) {
|
||||||
|
var payload = normalizePayload(value);
|
||||||
|
if (!payload) return [];
|
||||||
|
var data = payload.data;
|
||||||
|
if (data && !Array.isArray(data) && Array.isArray(data.data)) data = data.data;
|
||||||
|
if (Array.isArray(payload.items)) data = payload.items;
|
||||||
|
return Array.isArray(data) ? data.filter(function (item) { return item && typeof item === 'object'; }) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function idOf(item) { return String(item.id || item.code || item.key || item.number || ''); }
|
||||||
|
function titleOf(item) { return String(item.title || item.name || item.display_name || item.number || idOf(item) || '—'); }
|
||||||
|
function subtitleOf(item) { return String(item.subtitle || item.project_code || item.code || item.key || ''); }
|
||||||
|
function statusOf(item) {
|
||||||
|
var value = item.status;
|
||||||
|
if (value && typeof value === 'object') value = value.name || value.id;
|
||||||
|
return String(value || item.phase || '—');
|
||||||
|
}
|
||||||
|
function dateOf(item) { return String(item.date || item.due_date || item.updated_at || item.issued_at || item.created_at || ''); }
|
||||||
|
function valueOf(item) { return item.value === null || item.value === undefined ? '' : String(item.value); }
|
||||||
|
function dateText(value) {
|
||||||
|
if (!value) return '—';
|
||||||
|
var timestamp = Date.parse(value);
|
||||||
|
if (Number.isNaN(timestamp)) return value;
|
||||||
|
try { return new Intl.DateTimeFormat(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(new Date(timestamp)); }
|
||||||
|
catch (error) { return value; }
|
||||||
|
}
|
||||||
|
function make(tag, value, className) {
|
||||||
|
var node = document.createElement(tag);
|
||||||
|
if (className) node.className = className;
|
||||||
|
if (value !== undefined) node.textContent = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSummary() {
|
||||||
|
elements.summary.replaceChildren();
|
||||||
|
var item = make('span');
|
||||||
|
item.append(make('strong', String(records.length)), ' записей');
|
||||||
|
elements.summary.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRows() {
|
||||||
|
var query = elements.search.value.trim().toLowerCase();
|
||||||
|
var filtered = records.filter(function (item) {
|
||||||
|
if (!query) return true;
|
||||||
|
return (titleOf(item) + ' ' + subtitleOf(item) + ' ' + statusOf(item) + ' ' + idOf(item)).toLowerCase().indexOf(query) !== -1;
|
||||||
|
});
|
||||||
|
elements.rows.replaceChildren();
|
||||||
|
elements.empty.hidden = filtered.length !== 0;
|
||||||
|
filtered.forEach(function (item) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
var name = make('td');
|
||||||
|
name.append(make('span', titleOf(item), 'title'));
|
||||||
|
if (subtitleOf(item)) name.append(make('span', subtitleOf(item), 'subtitle'));
|
||||||
|
var status = make('td');
|
||||||
|
status.append(make('span', statusOf(item), 'pill'));
|
||||||
|
row.append(name, status, make('td', dateText(dateOf(item))), make('td', valueOf(item) || '—'));
|
||||||
|
row.className = 'record';
|
||||||
|
row.tabIndex = 0;
|
||||||
|
row.setAttribute('role', 'button');
|
||||||
|
row.setAttribute('aria-label', titleOf(item));
|
||||||
|
row.addEventListener('click', function () { showDetails(item); });
|
||||||
|
row.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') showDetails(item); });
|
||||||
|
elements.rows.append(row);
|
||||||
|
});
|
||||||
|
renderSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(value) {
|
||||||
|
records = toArray(value).slice().sort(function (left, right) {
|
||||||
|
var a = Date.parse(dateOf(left)); var b = Date.parse(dateOf(right));
|
||||||
|
if (Number.isNaN(a)) a = Number.POSITIVE_INFINITY;
|
||||||
|
if (Number.isNaN(b)) b = Number.POSITIVE_INFINITY;
|
||||||
|
return a - b || titleOf(left).localeCompare(titleOf(right));
|
||||||
|
});
|
||||||
|
renderRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(value, isError) {
|
||||||
|
elements.status.textContent = value || '';
|
||||||
|
elements.status.className = isError ? 'status-message error' : 'status-message';
|
||||||
|
}
|
||||||
|
|
||||||
|
function detailValue(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '—';
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
try { return JSON.stringify(value); } catch (error) { return '—'; }
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDetail(label, value) {
|
||||||
|
var item = make('div', undefined, 'detail');
|
||||||
|
item.append(make('span', label, 'detail-label'), make('span', detailValue(value), 'detail-value'));
|
||||||
|
elements.detailGrid.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetails(item) {
|
||||||
|
elements.detailsTitle.textContent = titleOf(item);
|
||||||
|
elements.detailsSubtitle.textContent = subtitleOf(item) || idOf(item);
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
addDetail('ID', idOf(item));
|
||||||
|
addDetail(CONFIG.statusLabel, statusOf(item));
|
||||||
|
addDetail(CONFIG.dateLabel, dateText(dateOf(item)));
|
||||||
|
if (valueOf(item)) addDetail(CONFIG.valueLabel, valueOf(item));
|
||||||
|
if (item.meta) addDetail('Meta', item.meta);
|
||||||
|
elements.details.hidden = false;
|
||||||
|
var id = idOf(item);
|
||||||
|
if (!id || !CONFIG.detailTool) return;
|
||||||
|
var args = {};
|
||||||
|
args[CONFIG.detailKey] = Number(id) || id;
|
||||||
|
if (CONFIG.detailSection) args.section = CONFIG.detailSection;
|
||||||
|
request('tools/call', { name: CONFIG.detailTool, arguments: args }).then(function (result) {
|
||||||
|
var payload = unwrap(result);
|
||||||
|
var data = payload && payload.data;
|
||||||
|
if (Array.isArray(data)) data = data[0];
|
||||||
|
if (!data || typeof data !== 'object') return;
|
||||||
|
elements.detailGrid.replaceChildren();
|
||||||
|
Object.keys(data).slice(0, 12).forEach(function (key) { addDetail(key, data[key]); });
|
||||||
|
}).catch(function (error) { setStatus(error && error.message ? error.message : 'Не удалось загрузить детали.', true); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (busy) return;
|
||||||
|
busy = true;
|
||||||
|
elements.refresh.disabled = true;
|
||||||
|
setStatus('Загрузка…', false);
|
||||||
|
request('tools/call', { name: CONFIG.listTool, arguments: CONFIG.listArgs }).then(function (result) {
|
||||||
|
render(unwrap(result));
|
||||||
|
setStatus('', false);
|
||||||
|
}).catch(function (error) {
|
||||||
|
setStatus(error && error.message ? error.message : 'Не удалось загрузить данные.', true);
|
||||||
|
}).finally(function () {
|
||||||
|
busy = false;
|
||||||
|
elements.refresh.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeHost() {
|
||||||
|
return request('ui/initialize', {
|
||||||
|
protocolVersion: '2025-11-25',
|
||||||
|
appInfo: { name: 'SHD ' + CONFIG.title, title: 'SHD ' + CONFIG.title, version: '1.0.0', websiteUrl: 'https://shd.xyz.su' },
|
||||||
|
appCapabilities: {}
|
||||||
|
}).then(function () { notify('ui/notifications/initialized', {}); }).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
var message = event.data;
|
||||||
|
if (!message || message.jsonrpc !== '2.0') return;
|
||||||
|
if (message.id !== undefined && pending.has(message.id)) {
|
||||||
|
var entry = pending.get(message.id);
|
||||||
|
pending.delete(message.id);
|
||||||
|
window.clearTimeout(entry.timeout);
|
||||||
|
if (message.error) entry.reject(new Error(message.error.message || 'Host request failed'));
|
||||||
|
else entry.resolve(message.result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-input') {
|
||||||
|
if (!records.length) render(message.params || {});
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-result') render(message.params && message.params.structuredContent);
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
elements.search.addEventListener('input', renderRows);
|
||||||
|
elements.refresh.addEventListener('click', refresh);
|
||||||
|
elements.close.addEventListener('click', function () { elements.details.hidden = true; });
|
||||||
|
void initializeHost();
|
||||||
|
var initialOutput = typeof window !== 'undefined' && window.openai ? window.openai.toolOutput : null;
|
||||||
|
if (initialOutput) render(initialOutput);
|
||||||
|
else {
|
||||||
|
var initialInput = typeof window !== 'undefined' && window.openai ? window.openai.toolInput : null;
|
||||||
|
if (initialInput) render(initialInput);
|
||||||
|
else refresh();
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-finance",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"uri": "ui://shd/finance/v1.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"bytes": 15532,
|
||||||
|
"sha256": "86a8d3275d1fdfaa6bbf5d1ab308b3e19a0b153897f614edd2c23d260030094e"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/finance/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e68a43f81a322387f71b947124d40a108881f9204600758a0db4ac374d3e3e04 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Gitea board widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/gitea-board/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Gitea issues grouped by state.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="gitea-board"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='gitea-board';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-gitea-board",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48775,
|
||||||
|
"gzipBytes": 13972,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/gitea-board/v1.html",
|
||||||
|
"sha256": "e68a43f81a322387f71b947124d40a108881f9204600758a0db4ac374d3e3e04"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/gitea-board/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
08a381ad106c5fa60d3c5c17c08949cd69f139c6fb5094251fa107f7c8bbaeb4 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Gitea Issues widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/gitea/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Read-only issue register for allowed targets.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='gitea';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){state.config.detailSourceArgs.forEach(function(name){if(state.config.sourceArgs&&state.config.sourceArgs[name]!==undefined)a[name]=state.config.sourceArgs[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code&&!a.project){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-gitea",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 25597,
|
||||||
|
"gzipBytes": 8708,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/gitea/v1.html",
|
||||||
|
"sha256": "08a381ad106c5fa60d3c5c17c08949cd69f139c6fb5094251fa107f7c8bbaeb4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/gitea/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
346c27c4b13871b31b2e66116a0ca85f916127dbcb73c3f5b4c5709ec209ea81 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Inventory warnings widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/inventory-warnings/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only low-stock and critical inventory dashboard.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="inventory-warnings"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='inventory-warnings';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-inventory-warnings",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48789,
|
||||||
|
"gzipBytes": 13974,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/inventory-warnings/v1.html",
|
||||||
|
"sha256": "346c27c4b13871b31b2e66116a0ca85f916127dbcb73c3f5b4c5709ec209ea81"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/inventory-warnings/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
684908987a55fd77f1b3d32dcd941b82163c03c04a48785589ccbb9ceeeb7f1f index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Inventory widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/inventory/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides stock quantities and read-only item details.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="inventory"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='inventory';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-inventory",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21067,
|
||||||
|
"gzipBytes": 7411,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/inventory/v1.html",
|
||||||
|
"sha256": "684908987a55fd77f1b3d32dcd941b82163c03c04a48785589ccbb9ceeeb7f1f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/inventory/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
00e5b692dd5d28f77ea210e6ee9f1c0ad07d68c978284f5380dc1db44f6d2107 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Notes tree widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/notes-tree/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Notes spaces and hierarchy tree.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes-tree"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='notes-tree';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextKeys:['base','project_code','table'],contextAll:true,sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-notes-tree",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48773,
|
||||||
|
"gzipBytes": 13976,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/notes-tree/v1.html",
|
||||||
|
"sha256": "00e5b692dd5d28f77ea210e6ee9f1c0ad07d68c978284f5380dc1db44f6d2107"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/notes-tree/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3aeb485b0b27cf89fe160b6e826d83f0027c01809756ef46701668a03105d321 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Notes widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/notes/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides page register and read-only page details.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='notes';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-notes",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21059,
|
||||||
|
"gzipBytes": 7411,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/notes/v1.html",
|
||||||
|
"sha256": "3aeb485b0b27cf89fe160b6e826d83f0027c01809756ef46701668a03105d321"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/notes/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8fd7b9cdb616836d076ae8a4883d66fcd619bf3214e7891fa0fcda4411dbf3e7 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Notifications widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/notifications/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides notifications and read-only event details.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notifications"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='notifications';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-notifications",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21075,
|
||||||
|
"gzipBytes": 7410,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/notifications/v1.html",
|
||||||
|
"sha256": "8fd7b9cdb616836d076ae8a4883d66fcd619bf3214e7891fa0fcda4411dbf3e7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/notifications/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
8059ccb18d02821666416ac717cd718479cb1a3f95baa27d1edfc19be5220c90 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Organizations and access widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/organizations/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Organization register with safe membership details.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='organizations';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){state.config.detailSourceArgs.forEach(function(name){if(state.config.sourceArgs&&state.config.sourceArgs[name]!==undefined)a[name]=state.config.sourceArgs[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code&&!a.project){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-organizations",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 25605,
|
||||||
|
"gzipBytes": 8711,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/organizations/v1.html",
|
||||||
|
"sha256": "8059ccb18d02821666416ac717cd718479cb1a3f95baa27d1edfc19be5220c90"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/organizations/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
b114986b9c03d02c63c5bcf8c0697615f4c80fa853bc9cf0bb5f6529357d90a4 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Project DB preview widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/project-db-preview/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Project DB table record preview.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="project-db-preview"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='project-db-preview';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']},
|
||||||
|
'task-kanban':{ru:['Задачи Kanban','Задачи по колонкам статусов с поиском и деталями.','Задача','Срок','Исполнитель'],en:['Task Kanban','Tasks grouped by workflow status with search and details.','Task','Due','Assignee']},
|
||||||
|
'team-workload':{ru:['Загрузка команды','Плановая нагрузка, ёмкость и утилизация команды.','Участник','Период','Часы или процент'],en:['Team workload','Planned workload, capacity and team utilization.','Member','Period','Hours or percent']},
|
||||||
|
'project-timeline':{ru:['Timeline проекта','Задачи проекта на временной ленте.','Задача','Срок','Статус'],en:['Project timeline','Project tasks on a chronological timeline.','Task','Due','Status']},
|
||||||
|
'crm-funnel':{ru:['CRM-воронка','Сделки CRM, сгруппированные по стадиям.','Стадия','Сделки','Сумма'],en:['CRM funnel','CRM deals grouped by pipeline stage.','Stage','Deals','Amount']},
|
||||||
|
'finance-dashboard':{ru:['Финансовый дашборд','Суммы, статусы и документы финансового модуля.','Документ','Обновлено','Сумма'],en:['Finance dashboard','Finance amounts, statuses and documents.','Document','Updated','Amount']},
|
||||||
|
'proposal-approvals':{ru:['Согласования предложений','Очередь предложений и текущие approval-статусы.','Предложение','Обновлено','Approval'],en:['Proposal approvals','Proposal queue and current approval states.','Proposal','Updated','Approval']},
|
||||||
|
'documents-completeness':{ru:['Полнота документов','Score, ревизии и состояние Terms-документов.','Документ','Обновлено','Полнота'],en:['Documents completeness','Scores, revisions and Terms document state.','Document','Updated','Completeness']},
|
||||||
|
'agents-health':{ru:['Здоровье агентов','Состояние агентов и контроллеров.','Агент или контроллер','Проверено','Health'],en:['Agents health','Agent and controller health state.','Agent or controller','Checked','Health']},
|
||||||
|
'schedule-calendar':{ru:['Календарь расписания','Ближайшие записи, сгруппированные по дням.','Событие','День','Участник'],en:['Schedule calendar','Upcoming bookings grouped by day.','Event','Day','Guest']},
|
||||||
|
'acl-matrix':{ru:['ACL-матрица','Участники организации, роли и permissions.','Участник','Роль','Права'],en:['ACL matrix','Organization members, roles and permissions.','Member','Role','Permissions']},
|
||||||
|
'inventory-warnings':{ru:['Предупреждения склада','Низкие остатки и критические складские позиции.','Позиция','Обновлено','Остаток'],en:['Inventory warnings','Low-stock and critical inventory items.','Item','Updated','Stock']},
|
||||||
|
'gitea-board':{ru:['Gitea-доска','Issues по состояниям разрешённого репозитория.','Issue','Состояние','Ссылка'],en:['Gitea board','Issues grouped by state for an allowed repository.','Issue','State','Link']},
|
||||||
|
'schemes-progress':{ru:['Прогресс схем','Документы схем, ревизии, страницы и completeness.','Схема','Обновлено','Прогресс'],en:['Schemes progress','Scheme documents, revisions, pages and completeness.','Scheme','Updated','Progress']},
|
||||||
|
'notes-tree':{ru:['Дерево Notes','Пространства Notes и их иерархия.','Узел','Обновлено','Тип'],en:['Notes tree','Notes spaces and their hierarchy.','Node','Updated','Type']},
|
||||||
|
'project-db-preview':{ru:['Project DB preview','Предпросмотр записей выбранной таблицы Project DB.','Запись','Обновлено','Поля'],en:['Project DB preview','Preview of records from the selected Project DB table.','Record','Updated','Fields']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
'task-kanban':{listTool:'shd_list_tasks',listArgs:{limit:100},readTool:'shd_get_task',detailKey:'task_id',detailSourceArgs:[],paths:['data'],view:'kanban',groupBy:'status',sort:'asc'},
|
||||||
|
'team-workload':{listTool:'shd_get_task_workload',listArgs:{period:'month'},paths:['data.rows','data.workload','data.assignees','data'],view:'workload',sort:'desc'},
|
||||||
|
'project-timeline':{listTool:'shd_list_tasks',listArgs:{limit:100},paths:['data'],view:'timeline',sort:'asc'},
|
||||||
|
'crm-funnel':{listTool:'shd_list_crm',listArgs:{section:'deals',limit:100},readTool:'shd_get_crm_entity',detailKey:'entity_id',detailSourceArgs:['section'],paths:['data'],view:'funnel',groupBy:'stage',sort:'desc'},
|
||||||
|
'finance-dashboard':{listTool:'shd_list_finance_documents',listArgs:{limit:100},paths:['data'],view:'dashboard',sort:'desc'},
|
||||||
|
'proposal-approvals':{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal_approval',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],view:'approval',sort:'desc'},
|
||||||
|
'documents-completeness':{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document_completeness',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],view:'completeness',contextRequired:true,contextKeys:['project_code'],sort:'desc'},
|
||||||
|
'agents-health':{listTool:'shd_agents_overview',paths:['data.controllers','data.agents','data.user_agents','data'],view:'health',sort:'asc'},
|
||||||
|
'schedule-calendar':{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],view:'calendar',sort:'asc'},
|
||||||
|
'acl-matrix':{listTool:'shd_list_organization_members',listArgs:{limit:100},paths:['data'],view:'matrix',contextRequired:true,contextKeys:['organization_id'],contextAll:true,sort:'asc'},
|
||||||
|
'inventory-warnings':{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',detailSourceArgs:[],paths:['data'],view:'warnings',sort:'asc'},
|
||||||
|
'gitea-board':{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],view:'kanban',groupBy:'state',sort:'asc'},
|
||||||
|
'schemes-progress':{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],view:'progress',contextRequired:true,contextKeys:['project'],sort:'desc'},
|
||||||
|
'notes-tree':{listTool:'shd_list_note_spaces',listArgs:{limit:100},readTool:'shd_get_note_space_tree',detailKey:'space_id',detailSourceArgs:[],paths:['data'],view:'tree',sort:'asc'},
|
||||||
|
'project-db-preview':{listTool:'shd_project_db_records',listArgs:{limit:100,include_fields:true,include_view_meta:true},paths:['data.records','data'],view:'preview',contextRequired:true,contextSets:[['base','project_code'],['table']],sort:'asc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',x.status||'—','pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',str(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',g),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Math.max.apply(null,Object.values(groups));var wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',g),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(g,counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'progress-card');card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day');day.append(make('div',d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return k+(typeof p[k]==='string'?'='+p[k]:'')}).join(', ')||'—';if(Array.isArray(p))return p.join(', ')||'—';return str(p)||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=make('tr');row.append(make('td',memberName(x)),make('td',str(field(x,['role','organization_role','type']))||'—'),make('td',status(x)||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};row.className='record';body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=make('article',undefined,'preview-card');card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',k+': '),make('span',str(x[k])));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-project-db-preview",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 48777,
|
||||||
|
"gzipBytes": 13982,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/project-db-preview/v1.html",
|
||||||
|
"sha256": "b114986b9c03d02c63c5bcf8c0697615f4c80fa853bc9cf0bb5f6529357d90a4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/project-db-preview/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bd34985f224325c18ae2427a49bf2355ee0d7009d47326000a10d9d7027effcb index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Project DB widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as `ui://shd/project-db/v1.html`.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides table catalog and read-only schema details.
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="project-db"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='project-db';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]);for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a)return flatten(a)}var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a=merge(state.config.sourceArgs,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-project-db",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 21069,
|
||||||
|
"gzipBytes": 7410,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/project-db/v1.html",
|
||||||
|
"sha256": "bd34985f224325c18ae2427a49bf2355ee0d7009d47326000a10d9d7027effcb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/project-db/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
adf8b6f9a96bf904cadd3eee70f4f18e870ca8e0e6286d4ad0d18f04f6174452 index.html
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Project overview widget
|
||||||
|
|
||||||
|
This artifact is served by the SHD MCP backend as ui://shd/project-overview/v1.html.
|
||||||
|
|
||||||
|
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Cross-module read-only project context.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-widget-module="notes"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='project-overview';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст проекта или базы'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A project or base context is required to refresh.'}};
|
||||||
|
var M={
|
||||||
|
discussions:{ru:['Обсуждения','Темы обсуждений с поиском и просмотром сообщений.','Тема','Обновлено','Сообщение'],en:['Discussions','Discussion topics with search and message details.','Topic','Updated','Message']},
|
||||||
|
notifications:{ru:['Уведомления','Последние уведомления и события модулей.','Уведомление','Время','Источник'],en:['Notifications','Recent notifications and module events.','Notification','Time','Source']},
|
||||||
|
scheduling:{ru:['Расписание','Ближайшие события и записи из расписания.','Событие','Начало','Участник'],en:['Scheduling','Upcoming events and bookings from scheduling.','Event','Starts','Guest']},
|
||||||
|
inventory:{ru:['Склад','Складские позиции, остатки и статусы.','Позиция','Обновлено','Количество'],en:['Inventory','Stock items, quantities and statuses.','Item','Updated','Quantity']},
|
||||||
|
agents:{ru:['Агенты и контроллеры','Доступные агенты, контроллеры и их текущее состояние.','Агент или контроллер','Проверено','Тип'],en:['Agents & Controllers','Available agents, controllers and their current state.','Agent or controller','Checked','Type']},
|
||||||
|
'status-page':{ru:['Status Page','Состояние доступности сервисов и мониторов.','Монитор','Проверено','Доступность'],en:['Status Page','Service availability and monitor state.','Monitor','Checked','Availability']},
|
||||||
|
'project-db':{ru:['Project DB','Каталог таблиц Project DB с просмотром схемы.','Таблица','Обновлено','Записей'],en:['Project DB','Project DB table catalog with schema details.','Table','Updated','Records']},
|
||||||
|
notes:{ru:['Заметки','Страницы Notes с поиском и просмотром содержимого.','Страница','Обновлено','Пространство'],en:['Notes','Notes pages with search and content details.','Page','Updated','Space']},
|
||||||
|
'project-overview':{ru:['Обзор проекта','Сводные данные по разделам выбранного проекта.','Раздел','Обновлено','Элементы'],en:['Project overview','Cross-module records for the selected project.','Section','Updated','Items']},
|
||||||
|
files:{ru:['Файлы','Файлы выбранного проекта с поиском и безопасным просмотром ссылок.','Файл','Обновлено','Размер или тип'],en:['Files','Files from the selected project with search and safe links.','File','Updated','Size or type']},
|
||||||
|
proposals:{ru:['Предложения','Предложения и сметы с поиском, статусами и деталями.','Предложение','Обновлено','Сумма или номер'],en:['Proposals','Proposals with search, statuses and details.','Proposal','Updated','Amount or number']},
|
||||||
|
terms:{ru:['Terms / ТЗ','Документы технического задания и их ревизии.','Документ','Обновлено','Страницы или ревизия'],en:['Terms documents','Terms of Reference documents and revisions.','Document','Updated','Pages or revision']},
|
||||||
|
activity:{ru:['Активность','Авторизованные realtime-события SHD по выбранному scope.','Событие','Время','Модуль или тип'],en:['Activity','Authenticated SHD realtime events for the selected scope.','Event','Time','Module or type']},
|
||||||
|
organizations:{ru:['Организации и доступ','Организации, роли и безопасный просмотр участников.','Организация','Обновлено','Роль или тип'],en:['Organizations & access','Organizations, roles and safe membership details.','Organization','Updated','Role or type']},
|
||||||
|
gitea:{ru:['Gitea Issues','Открытые задачи разрешённого site или project репозитория.','Issue','Обновлено','Состояние'],en:['Gitea Issues','Open issues for an allowed site or project repository.','Issue','Updated','State']},
|
||||||
|
schemes:{ru:['Схемы','Документы PDF Scheme Editor и их текущее состояние.','Схема','Обновлено','Ревизия или страницы'],en:['Schemes','PDF Scheme Editor documents and their current state.','Scheme','Updated','Revision or pages']}
|
||||||
|
};
|
||||||
|
var S={
|
||||||
|
discussions:{listTool:'shd_list_discussion_topics',listArgs:{limit:100},readTool:'shd_list_discussion_messages',detailKey:'topic_id',detailArgs:{limit:50},paths:['data'],sort:'desc'},
|
||||||
|
notifications:{listTool:'shd_list_notifications',listArgs:{limit:50,offset:0},paths:['data'],sort:'desc'},
|
||||||
|
scheduling:{listTool:'shd_get_scheduling_overview',listArgs:{limit:100},readTool:'shd_list_scheduling_bookings',detailKey:'booking_id',detailArgs:{limit:1},paths:['data.bookings','data.upcoming_bookings','data'],sort:'asc'},
|
||||||
|
inventory:{listTool:'shd_list_inventory_items',listArgs:{limit:100},readTool:'shd_get_inventory_item',detailKey:'item_id',paths:['data'],sort:'desc'},
|
||||||
|
agents:{listTool:'shd_agents_overview',listArgs:{},paths:['data.controllers','data.agents','data.user_agents','data'],sort:'asc'},
|
||||||
|
'status-page':{listTool:'shd_get_status_page_monitors',listArgs:{environment:'dev'},readTool:'shd_get_status_page_monitor_history',detailKey:'monitor_id',detailArgs:{limit:20},paths:['data'],nested:'monitors',sort:'asc'},
|
||||||
|
'project-db':{listTool:'shd_project_db_tables',listArgs:{},readTool:'shd_project_db_schema',detailKey:'table',paths:['data.tables','data'],contextRequired:true,sort:'asc'},
|
||||||
|
notes:{listTool:'shd_list_note_pages',listArgs:{limit:100},readTool:'shd_get_note_page',detailKey:'page_id',paths:['data'],sort:'desc'},
|
||||||
|
'project-overview':{listTool:'shd_get_project_context',listArgs:{include:['summary','tasks','discussions','documents','finance','terms','agents'],limit_per_section:100},paths:['data.sections.tasks','data.sections.discussions','data.sections.documents','data.sections.finance','data.sections.terms'],combinePaths:true,contextRequired:true,sort:'desc'},
|
||||||
|
files:{listTool:'shd_list_files',readTool:'shd_get_file_link',detailKey:'path',detailArgs:{download:false},detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
proposals:{listTool:'shd_list_proposals',listArgs:{limit:100},readTool:'shd_get_proposal',detailKey:'proposal_id',detailSourceArgs:[],paths:['data'],sort:'desc'},
|
||||||
|
terms:{listTool:'shd_list_terms_documents',readTool:'shd_get_terms_document',detailKey:'document_id',detailSourceArgs:['project_code'],paths:['data'],contextRequired:true,sort:'desc'},
|
||||||
|
activity:{listTool:'shd_get_realtime_events',listArgs:{module:'notifications',limit:100},paths:['data.events','events','data'],sort:'desc'},
|
||||||
|
organizations:{listTool:'shd_admin_list_organizations',listArgs:{limit:100},readTool:'shd_admin_get_organization_members',detailKey:'organization_id',detailSourceArgs:[],paths:['data'],sort:'asc'},
|
||||||
|
gitea:{listTool:'shd_list_gitea_issues',listArgs:{target:'site',limit:100},paths:['data'],sort:'desc'},
|
||||||
|
schemes:{listTool:'shd_list_scheme_documents',readTool:'shd_get_scheme_document',detailKey:'document',detailSourceArgs:['project'],paths:['data'],contextRequired:true,sort:'desc'}
|
||||||
|
};
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function detailText(x){return x===null||x===undefined||x===''?'—':str(x)}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+k);e.summary.append(n)})}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',x.status||'—','pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));r.append(n,make('td').appendChild(s).parentNode,make('td',dateText(x.date)),make('td',x.value||'—'));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});renderRows()}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id);if(x.status)add(t('status'),x.status);if(x.date)add(t('date'),dateText(x.date));if(x.value)add(t('value'),x.value);Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(k,x[k])});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){state.config.detailSourceArgs.forEach(function(name){if(state.config.sourceArgs&&state.config.sourceArgs[name]!==undefined)a[name]=state.config.sourceArgs[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;e.grid.replaceChildren();if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(q,d[q])});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(state.config.contextRequired&&!a.base&&!a.project_code&&!a.project){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-project-overview",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"bytes": 25608,
|
||||||
|
"gzipBytes": 8709,
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/project-overview/v1.html",
|
||||||
|
"sha256": "adf8b6f9a96bf904cadd3eee70f4f18e870ca8e0e6286d4ad0d18f04f6174452"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"path": "plugins/shd-mcp-plugin/widgets/project-overview/v1/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user