snow-flow 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-flow/queen/queen-memory.db +0 -0
- package/.env.example +249 -14
- package/CLAUDE.md +185 -24
- package/README.md +55 -5
- package/dist/api/error-handling.js +898 -4
- package/dist/api/performance-optimizer.js +3 -2
- package/dist/cli.js +590 -200
- package/dist/config/snow-flow-config.js +320 -8
- package/dist/health/system-health.js +288 -21
- package/dist/mcp/http-transport-wrapper.js +65 -4
- package/dist/mcp/servicenow-automation-mcp-refactored.js +90 -9
- package/dist/mcp/servicenow-deployment-mcp-refactored.js +151 -2
- package/dist/mcp/servicenow-deployment-mcp.js +828 -15
- package/dist/mcp/servicenow-integration-mcp-refactored.js +100 -9
- package/dist/mcp/servicenow-intelligent-mcp.js +198 -7
- package/dist/mcp/servicenow-memory-mcp.js +2 -1
- package/dist/mcp/servicenow-operations-mcp-refactored.js +3 -2
- package/dist/mcp/servicenow-xml-flow-mcp.js +671 -0
- package/dist/mcp/shared/base-mcp-server.js +1356 -8
- package/dist/mcp/shared/mcp-resource-manager.js +304 -0
- package/dist/queen/parallel-agent-engine.js +26 -8
- package/dist/queen/servicenow-queen.js +47 -44
- package/dist/sparc/sparc-help.js +37 -26
- package/dist/sparc/team-sparc.js +60 -16
- package/dist/utils/action-type-cache.js +2 -1
- package/dist/utils/mcp-config-manager.js +7 -3
- package/dist/utils/mcp-server-manager.js +7 -3
- package/dist/utils/servicenow-client.js +134 -107
- package/dist/utils/servicenow-id-generator.js +171 -0
- package/dist/utils/snow-oauth.js +13 -8
- package/dist/utils/widget-template-generator.js +1690 -0
- package/dist/utils/xml-first-flow-generator.js +473 -0
- package/flow-update-sets/flow_build_automated_approval_flow_with_notifications_flow.xml +203 -0
- package/flow-update-sets/flow_create_approval_flow_for_equipment_requests_flow.xml +206 -0
- package/flow-update-sets/flow_create_equipment_approval_flow_with_manager_approv_flow.xml +254 -0
- package/flow-update-sets/iphone_15_pro_approval_flow.xml +203 -0
- package/flow-update-sets/test_iphone_approval_flow_flow.xml +303 -0
- package/package.json +2 -1
- package/test-config.js +55 -0
- package/test-real-monitoring.js +184 -0
|
@@ -14,6 +14,7 @@ const logger_js_1 = require("../utils/logger.js");
|
|
|
14
14
|
const scope_manager_js_1 = require("../managers/scope-manager.js");
|
|
15
15
|
const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
|
|
16
16
|
const artifact_tracker_js_1 = require("../utils/artifact-tracker.js");
|
|
17
|
+
const servicenow_id_generator_js_1 = require("../utils/servicenow-id-generator.js");
|
|
17
18
|
const fs_1 = require("fs");
|
|
18
19
|
const path_1 = require("path");
|
|
19
20
|
class ServiceNowDeploymentMCP {
|
|
@@ -2251,12 +2252,7 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
|
|
|
2251
2252
|
* Generate ServiceNow-style GUID
|
|
2252
2253
|
*/
|
|
2253
2254
|
generateGUID() {
|
|
2254
|
-
|
|
2255
|
-
let result = '';
|
|
2256
|
-
for (let i = 0; i < 32; i++) {
|
|
2257
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
2258
|
-
}
|
|
2259
|
-
return result;
|
|
2255
|
+
return (0, servicenow_id_generator_js_1.generateServiceNowSysId)();
|
|
2260
2256
|
}
|
|
2261
2257
|
/**
|
|
2262
2258
|
* Generate Update Set XML based on artifact type
|
|
@@ -2627,14 +2623,117 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
|
|
|
2627
2623
|
</sys_update_xml>`;
|
|
2628
2624
|
}
|
|
2629
2625
|
/**
|
|
2630
|
-
* Generate Update Set XML for applications
|
|
2626
|
+
* Generate Update Set XML for applications
|
|
2631
2627
|
*/
|
|
2632
2628
|
generateApplicationUpdateSetXML(args) {
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2629
|
+
const timestamp = new Date().toISOString();
|
|
2630
|
+
const updateSetName = `Application_${args.name}_${Date.now()}`;
|
|
2631
|
+
// Generate unique identifiers
|
|
2632
|
+
const updateSetId = this.generateGUID();
|
|
2633
|
+
const applicationId = this.generateGUID();
|
|
2634
|
+
const updateXmlId = this.generateGUID();
|
|
2635
|
+
// Default values
|
|
2636
|
+
const applicationData = {
|
|
2637
|
+
name: args.name || 'New Application',
|
|
2638
|
+
short_description: args.short_description || args.description || 'Custom Application',
|
|
2639
|
+
description: args.description || 'Generated by Snow-Flow',
|
|
2640
|
+
version: args.version || '1.0.0',
|
|
2641
|
+
vendor: args.vendor || 'Snow-Flow',
|
|
2642
|
+
vendor_prefix: args.vendor_prefix || 'x_snf',
|
|
2643
|
+
active: args.active !== false,
|
|
2644
|
+
scope: args.scope || 'x_snf_' + args.name?.toLowerCase().replace(/[^a-z0-9]/g, '_'),
|
|
2645
|
+
logo: args.logo || '',
|
|
2646
|
+
roles: args.roles || ''
|
|
2647
|
+
};
|
|
2648
|
+
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
2649
|
+
<unload unload_date="${timestamp}">
|
|
2650
|
+
<sys_update_set action="INSERT_OR_UPDATE">
|
|
2651
|
+
<application display_value="Global">global</application>
|
|
2652
|
+
<category>customer</category>
|
|
2653
|
+
<description>Auto-generated Update Set for Application: ${applicationData.name}</description>
|
|
2654
|
+
<is_default>false</is_default>
|
|
2655
|
+
<name>${updateSetName}</name>
|
|
2656
|
+
<origin_sys_id/>
|
|
2657
|
+
<release_date/>
|
|
2658
|
+
<state>complete</state>
|
|
2659
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2660
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2661
|
+
<sys_id>${updateSetId}</sys_id>
|
|
2662
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2663
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2664
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2665
|
+
<update_count>1</update_count>
|
|
2666
|
+
</sys_update_set>
|
|
2667
|
+
|
|
2668
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
2669
|
+
<action>INSERT_OR_UPDATE</action>
|
|
2670
|
+
<application display_value="Global">global</application>
|
|
2671
|
+
<category>customer</category>
|
|
2672
|
+
<name>sys_app_${applicationId}</name>
|
|
2673
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_app"><sys_app action="INSERT_OR_UPDATE">
|
|
2674
|
+
<active>${applicationData.active}</active>
|
|
2675
|
+
<can_edit_in_studio>true</can_edit_in_studio>
|
|
2676
|
+
<category>Custom</category>
|
|
2677
|
+
<description>${this.escapeXml(applicationData.description)}</description>
|
|
2678
|
+
<enforce_license>none</enforce_license>
|
|
2679
|
+
<guided_setup_guid/>
|
|
2680
|
+
<hide_on_ui>false</hide_on_ui>
|
|
2681
|
+
<installed_as_dependency>false</installed_as_dependency>
|
|
2682
|
+
<js_level>helsinki_es5</js_level>
|
|
2683
|
+
<licensable>true</licensable>
|
|
2684
|
+
<license_category>none</license_category>
|
|
2685
|
+
<license_definition/>
|
|
2686
|
+
<license_model>none</license_model>
|
|
2687
|
+
<logo display_value="">${applicationData.logo}</logo>
|
|
2688
|
+
<menu/>
|
|
2689
|
+
<name>${this.escapeXml(applicationData.name)}</name>
|
|
2690
|
+
<private>false</private>
|
|
2691
|
+
<restrict_table_access>false</restrict_table_access>
|
|
2692
|
+
<runtime_access_tracking>permissive</runtime_access_tracking>
|
|
2693
|
+
<scope>${applicationData.scope}</scope>
|
|
2694
|
+
<scoped_administration>false</scoped_administration>
|
|
2695
|
+
<short_description>${this.escapeXml(applicationData.short_description)}</short_description>
|
|
2696
|
+
<source>${applicationData.scope}</source>
|
|
2697
|
+
<store_correlation_id/>
|
|
2698
|
+
<store_url/>
|
|
2699
|
+
<sys_class_name>sys_app</sys_class_name>
|
|
2700
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2701
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2702
|
+
<sys_id>${applicationId}</sys_id>
|
|
2703
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2704
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2705
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2706
|
+
<template/>
|
|
2707
|
+
<trackable>true</trackable>
|
|
2708
|
+
<uninstall_blocked>false</uninstall_blocked>
|
|
2709
|
+
<user_role display_value="">${applicationData.roles}</user_role>
|
|
2710
|
+
<vendor display_value="${applicationData.vendor}">${applicationData.vendor}</vendor>
|
|
2711
|
+
<vendor_prefix>${applicationData.vendor_prefix}</vendor_prefix>
|
|
2712
|
+
<version>${applicationData.version}</version>
|
|
2713
|
+
</sys_app></record_update>]]></payload>
|
|
2714
|
+
<payload_hash>-1</payload_hash>
|
|
2715
|
+
<record_name>${applicationData.name}</record_name>
|
|
2716
|
+
<reverted_from/>
|
|
2717
|
+
<source display_value="sys_app.${applicationId}">sys_app.${applicationId}</source>
|
|
2718
|
+
<source_table>sys_app</source_table>
|
|
2719
|
+
<state>previous</state>
|
|
2720
|
+
<sys_created_by>snow-flow</sys_created_by>
|
|
2721
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2722
|
+
<sys_id>${updateXmlId}</sys_id>
|
|
2723
|
+
<sys_mod_count>0</sys_mod_count>
|
|
2724
|
+
<sys_updated_by>snow-flow</sys_updated_by>
|
|
2725
|
+
<sys_updated_on>${timestamp}</sys_updated_on>
|
|
2726
|
+
<table>sys_app</table>
|
|
2727
|
+
<target_name>${applicationData.name}</target_name>
|
|
2728
|
+
<type>Application File</type>
|
|
2729
|
+
<update_domain>global</update_domain>
|
|
2730
|
+
<update_guid>${this.generateGUID()}</update_guid>
|
|
2731
|
+
<update_guid_history>${this.generateGUID()}</update_guid_history>
|
|
2732
|
+
<update_set display_value="${updateSetName}">${updateSetId}</update_set>
|
|
2733
|
+
<view/>
|
|
2734
|
+
</sys_update_xml>
|
|
2637
2735
|
</unload>`;
|
|
2736
|
+
return xmlContent;
|
|
2638
2737
|
}
|
|
2639
2738
|
/**
|
|
2640
2739
|
* Preview widget with test data
|
|
@@ -4382,10 +4481,10 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
4382
4481
|
name: this.extractNameFromInstruction(instruction),
|
|
4383
4482
|
title: this.extractNameFromInstruction(instruction),
|
|
4384
4483
|
description: instruction,
|
|
4385
|
-
template:
|
|
4386
|
-
css:
|
|
4387
|
-
server_script:
|
|
4388
|
-
client_script:
|
|
4484
|
+
template: this.generateWidgetTemplate(instruction),
|
|
4485
|
+
css: this.generateWidgetCss(instruction),
|
|
4486
|
+
server_script: this.generateWidgetServerScript(instruction),
|
|
4487
|
+
client_script: this.generateWidgetClientScript(instruction),
|
|
4389
4488
|
instruction: instruction
|
|
4390
4489
|
};
|
|
4391
4490
|
}
|
|
@@ -4405,6 +4504,720 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
4405
4504
|
!['the', 'and', 'for', 'with', 'that', 'will', 'can', 'should'].includes(word)).slice(0, 3).join('_');
|
|
4406
4505
|
return name || 'auto_generated_artifact';
|
|
4407
4506
|
}
|
|
4507
|
+
/**
|
|
4508
|
+
* Generate HTML template based on widget requirements
|
|
4509
|
+
*/
|
|
4510
|
+
generateWidgetTemplate(instruction) {
|
|
4511
|
+
const lower = instruction.toLowerCase();
|
|
4512
|
+
const title = this.extractNameFromInstruction(instruction).replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
4513
|
+
// Chart/Graph widgets
|
|
4514
|
+
if (lower.includes('chart') || lower.includes('graph') || lower.includes('analytics')) {
|
|
4515
|
+
return `
|
|
4516
|
+
<div class="panel panel-default widget-chart">
|
|
4517
|
+
<div class="panel-heading">
|
|
4518
|
+
<h3 class="panel-title">{{data.title || '${title}'}}</h3>
|
|
4519
|
+
<div class="panel-actions" ng-if="data.refresh_enabled">
|
|
4520
|
+
<button class="btn btn-sm btn-default" ng-click="c.refreshData()">
|
|
4521
|
+
<i class="fa fa-refresh"></i> Refresh
|
|
4522
|
+
</button>
|
|
4523
|
+
</div>
|
|
4524
|
+
</div>
|
|
4525
|
+
<div class="panel-body">
|
|
4526
|
+
<div ng-if="data.loading" class="text-center">
|
|
4527
|
+
<i class="fa fa-spinner fa-spin"></i> Loading chart data...
|
|
4528
|
+
</div>
|
|
4529
|
+
<canvas ng-if="!data.loading" id="chart-{{::data.widget_id}}" width="400" height="300"></canvas>
|
|
4530
|
+
<div ng-if="data.error" class="alert alert-danger">
|
|
4531
|
+
<i class="fa fa-exclamation-triangle"></i> {{data.error}}
|
|
4532
|
+
</div>
|
|
4533
|
+
</div>
|
|
4534
|
+
</div>`;
|
|
4535
|
+
}
|
|
4536
|
+
// Dashboard widgets
|
|
4537
|
+
if (lower.includes('dashboard') || lower.includes('metrics') || lower.includes('kpi')) {
|
|
4538
|
+
return `
|
|
4539
|
+
<div class="row widget-dashboard">
|
|
4540
|
+
<div class="col-md-12">
|
|
4541
|
+
<div class="panel panel-default">
|
|
4542
|
+
<div class="panel-heading">
|
|
4543
|
+
<h3 class="panel-title">{{data.title || '${title}'}}</h3>
|
|
4544
|
+
<span class="panel-subtitle" ng-if="data.last_updated">
|
|
4545
|
+
Last updated: {{data.last_updated | date:'medium'}}
|
|
4546
|
+
</span>
|
|
4547
|
+
</div>
|
|
4548
|
+
<div class="panel-body">
|
|
4549
|
+
<div class="row">
|
|
4550
|
+
<div class="col-md-{{12/data.metrics.length}}" ng-repeat="metric in data.metrics track by $index">
|
|
4551
|
+
<div class="metric-card well text-center" ng-class="{'metric-critical': metric.critical, 'metric-warning': metric.warning}">
|
|
4552
|
+
<div class="metric-icon" ng-if="metric.icon">
|
|
4553
|
+
<i class="fa fa-{{metric.icon}}"></i>
|
|
4554
|
+
</div>
|
|
4555
|
+
<h4 class="metric-label">{{metric.label}}</h4>
|
|
4556
|
+
<h2 class="metric-value" ng-class="{'text-danger': metric.critical, 'text-warning': metric.warning, 'text-success': metric.success}">
|
|
4557
|
+
{{metric.value}} <small ng-if="metric.unit">{{metric.unit}}</small>
|
|
4558
|
+
</h2>
|
|
4559
|
+
<div class="metric-trend" ng-if="metric.trend">
|
|
4560
|
+
<i class="fa" ng-class="{'fa-arrow-up text-success': metric.trend > 0, 'fa-arrow-down text-danger': metric.trend < 0, 'fa-minus text-muted': metric.trend === 0}"></i>
|
|
4561
|
+
<span class="trend-value">{{metric.trend_text}}</span>
|
|
4562
|
+
</div>
|
|
4563
|
+
</div>
|
|
4564
|
+
</div>
|
|
4565
|
+
</div>
|
|
4566
|
+
</div>
|
|
4567
|
+
</div>
|
|
4568
|
+
</div>
|
|
4569
|
+
</div>`;
|
|
4570
|
+
}
|
|
4571
|
+
// Table/List widgets
|
|
4572
|
+
if (lower.includes('table') || lower.includes('list') || lower.includes('records')) {
|
|
4573
|
+
return `
|
|
4574
|
+
<div class="panel panel-default widget-table">
|
|
4575
|
+
<div class="panel-heading">
|
|
4576
|
+
<h3 class="panel-title">{{data.title || '${title}'}}</h3>
|
|
4577
|
+
<div class="panel-actions">
|
|
4578
|
+
<input type="text" class="form-control input-sm" placeholder="Search..." ng-model="c.searchTerm" ng-if="data.search_enabled">
|
|
4579
|
+
<button class="btn btn-sm btn-primary" ng-click="c.refresh()" ng-if="data.refresh_enabled">
|
|
4580
|
+
<i class="fa fa-refresh"></i>
|
|
4581
|
+
</button>
|
|
4582
|
+
</div>
|
|
4583
|
+
</div>
|
|
4584
|
+
<div class="panel-body">
|
|
4585
|
+
<div ng-if="data.loading" class="text-center">
|
|
4586
|
+
<i class="fa fa-spinner fa-spin"></i> Loading data...
|
|
4587
|
+
</div>
|
|
4588
|
+
<div ng-if="!data.loading && data.records.length > 0">
|
|
4589
|
+
<table class="table table-striped table-hover">
|
|
4590
|
+
<thead>
|
|
4591
|
+
<tr>
|
|
4592
|
+
<th ng-repeat="column in data.columns" ng-click="c.sortBy(column.field)" class="sortable">
|
|
4593
|
+
{{column.label}}
|
|
4594
|
+
<i class="fa fa-sort" ng-if="c.sortField !== column.field"></i>
|
|
4595
|
+
<i class="fa fa-sort-up" ng-if="c.sortField === column.field && !c.sortReverse"></i>
|
|
4596
|
+
<i class="fa fa-sort-down" ng-if="c.sortField === column.field && c.sortReverse"></i>
|
|
4597
|
+
</th>
|
|
4598
|
+
</tr>
|
|
4599
|
+
</thead>
|
|
4600
|
+
<tbody>
|
|
4601
|
+
<tr ng-repeat="record in data.records | filter:c.searchTerm | orderBy:c.sortField:c.sortReverse track by record.sys_id">
|
|
4602
|
+
<td ng-repeat="column in data.columns">
|
|
4603
|
+
<span ng-if="column.type === 'link'">
|
|
4604
|
+
<a href="{{record[column.field + '_link']}}" target="_blank">{{record[column.field]}}</a>
|
|
4605
|
+
</span>
|
|
4606
|
+
<span ng-if="column.type === 'date'">
|
|
4607
|
+
{{record[column.field] | date:'short'}}
|
|
4608
|
+
</span>
|
|
4609
|
+
<span ng-if="!column.type || (column.type !== 'link' && column.type !== 'date')">
|
|
4610
|
+
{{record[column.field]}}
|
|
4611
|
+
</span>
|
|
4612
|
+
</td>
|
|
4613
|
+
</tr>
|
|
4614
|
+
</tbody>
|
|
4615
|
+
</table>
|
|
4616
|
+
</div>
|
|
4617
|
+
<div ng-if="!data.loading && data.records.length === 0" class="alert alert-info text-center">
|
|
4618
|
+
<i class="fa fa-info-circle"></i> No records found
|
|
4619
|
+
</div>
|
|
4620
|
+
</div>
|
|
4621
|
+
</div>`;
|
|
4622
|
+
}
|
|
4623
|
+
// Form widgets
|
|
4624
|
+
if (lower.includes('form') || lower.includes('input') || lower.includes('create') || lower.includes('submit')) {
|
|
4625
|
+
return `
|
|
4626
|
+
<div class="panel panel-default widget-form">
|
|
4627
|
+
<div class="panel-heading">
|
|
4628
|
+
<h3 class="panel-title">{{data.title || '${title}'}}</h3>
|
|
4629
|
+
</div>
|
|
4630
|
+
<div class="panel-body">
|
|
4631
|
+
<form name="widgetForm" ng-submit="c.submitForm()" novalidate>
|
|
4632
|
+
<div class="form-group" ng-repeat="field in data.fields">
|
|
4633
|
+
<label class="control-label" for="{{field.name}}">
|
|
4634
|
+
{{field.label}}
|
|
4635
|
+
<span class="text-danger" ng-if="field.required">*</span>
|
|
4636
|
+
</label>
|
|
4637
|
+
<input ng-if="field.type === 'text'"
|
|
4638
|
+
type="text"
|
|
4639
|
+
class="form-control"
|
|
4640
|
+
id="{{field.name}}"
|
|
4641
|
+
name="{{field.name}}"
|
|
4642
|
+
ng-model="c.formData[field.name]"
|
|
4643
|
+
ng-required="field.required"
|
|
4644
|
+
placeholder="{{field.placeholder}}">
|
|
4645
|
+
<textarea ng-if="field.type === 'textarea'"
|
|
4646
|
+
class="form-control"
|
|
4647
|
+
id="{{field.name}}"
|
|
4648
|
+
name="{{field.name}}"
|
|
4649
|
+
ng-model="c.formData[field.name]"
|
|
4650
|
+
ng-required="field.required"
|
|
4651
|
+
placeholder="{{field.placeholder}}"
|
|
4652
|
+
rows="3"></textarea>
|
|
4653
|
+
<select ng-if="field.type === 'select'"
|
|
4654
|
+
class="form-control"
|
|
4655
|
+
id="{{field.name}}"
|
|
4656
|
+
name="{{field.name}}"
|
|
4657
|
+
ng-model="c.formData[field.name]"
|
|
4658
|
+
ng-required="field.required">
|
|
4659
|
+
<option value="">Select {{field.label}}</option>
|
|
4660
|
+
<option ng-repeat="option in field.options" value="{{option.value}}">{{option.label}}</option>
|
|
4661
|
+
</select>
|
|
4662
|
+
<div class="text-danger" ng-if="widgetForm[field.name].$invalid && widgetForm[field.name].$touched">
|
|
4663
|
+
<small ng-if="widgetForm[field.name].$error.required">{{field.label}} is required</small>
|
|
4664
|
+
</div>
|
|
4665
|
+
</div>
|
|
4666
|
+
<div class="form-actions">
|
|
4667
|
+
<button type="submit" class="btn btn-primary" ng-disabled="widgetForm.$invalid || c.submitting">
|
|
4668
|
+
<i class="fa fa-spinner fa-spin" ng-if="c.submitting"></i>
|
|
4669
|
+
{{c.submitting ? 'Submitting...' : 'Submit'}}
|
|
4670
|
+
</button>
|
|
4671
|
+
<button type="button" class="btn btn-default" ng-click="c.resetForm()">Reset</button>
|
|
4672
|
+
</div>
|
|
4673
|
+
</form>
|
|
4674
|
+
</div>
|
|
4675
|
+
</div>`;
|
|
4676
|
+
}
|
|
4677
|
+
// Generic/Default widget template
|
|
4678
|
+
return `
|
|
4679
|
+
<div class="panel panel-default widget-generic">
|
|
4680
|
+
<div class="panel-heading">
|
|
4681
|
+
<h3 class="panel-title">{{data.title || '${title}'}}</h3>
|
|
4682
|
+
</div>
|
|
4683
|
+
<div class="panel-body">
|
|
4684
|
+
<div ng-if="data.items && data.items.length > 0">
|
|
4685
|
+
<div class="list-group">
|
|
4686
|
+
<div class="list-group-item" ng-repeat="item in data.items track by $index">
|
|
4687
|
+
<div class="list-group-item-heading" ng-if="item.title">
|
|
4688
|
+
<strong>{{item.title}}</strong>
|
|
4689
|
+
</div>
|
|
4690
|
+
<div class="list-group-item-text" ng-if="item.description">
|
|
4691
|
+
{{item.description}}
|
|
4692
|
+
</div>
|
|
4693
|
+
<div class="item-meta" ng-if="item.meta">
|
|
4694
|
+
<small class="text-muted">{{item.meta}}</small>
|
|
4695
|
+
</div>
|
|
4696
|
+
</div>
|
|
4697
|
+
</div>
|
|
4698
|
+
</div>
|
|
4699
|
+
<div ng-if="!data.items || data.items.length === 0" class="text-center text-muted">
|
|
4700
|
+
<i class="fa fa-inbox fa-3x"></i>
|
|
4701
|
+
<h4>{{data.empty_message || 'No data available'}}</h4>
|
|
4702
|
+
<p ng-if="data.empty_description">{{data.empty_description}}</p>
|
|
4703
|
+
</div>
|
|
4704
|
+
</div>
|
|
4705
|
+
</div>`;
|
|
4706
|
+
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Generate CSS styles based on widget requirements
|
|
4709
|
+
*/
|
|
4710
|
+
generateWidgetCss(instruction) {
|
|
4711
|
+
const lower = instruction.toLowerCase();
|
|
4712
|
+
// Base styles for all widgets
|
|
4713
|
+
let css = `
|
|
4714
|
+
.panel {
|
|
4715
|
+
margin-bottom: 20px;
|
|
4716
|
+
border-radius: 6px;
|
|
4717
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
.panel-heading {
|
|
4721
|
+
background: linear-gradient(to bottom, #f8f9fa 0%, #e9ecef 100%);
|
|
4722
|
+
border-bottom: 1px solid #dee2e6;
|
|
4723
|
+
position: relative;
|
|
4724
|
+
}
|
|
4725
|
+
|
|
4726
|
+
.panel-title {
|
|
4727
|
+
margin: 0;
|
|
4728
|
+
font-size: 16px;
|
|
4729
|
+
font-weight: 600;
|
|
4730
|
+
color: #495057;
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
.panel-actions {
|
|
4734
|
+
position: absolute;
|
|
4735
|
+
right: 15px;
|
|
4736
|
+
top: 50%;
|
|
4737
|
+
transform: translateY(-50%);
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4740
|
+
.panel-body {
|
|
4741
|
+
padding: 15px;
|
|
4742
|
+
}
|
|
4743
|
+
`;
|
|
4744
|
+
// Chart-specific styles
|
|
4745
|
+
if (lower.includes('chart') || lower.includes('graph')) {
|
|
4746
|
+
css += `
|
|
4747
|
+
.widget-chart canvas {
|
|
4748
|
+
max-width: 100%;
|
|
4749
|
+
height: auto;
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
.widget-chart .panel-actions button {
|
|
4753
|
+
margin-left: 5px;
|
|
4754
|
+
}
|
|
4755
|
+
`;
|
|
4756
|
+
}
|
|
4757
|
+
// Dashboard-specific styles
|
|
4758
|
+
if (lower.includes('dashboard') || lower.includes('metrics')) {
|
|
4759
|
+
css += `
|
|
4760
|
+
.widget-dashboard .metric-card {
|
|
4761
|
+
margin-bottom: 15px;
|
|
4762
|
+
transition: all 0.3s ease;
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
.widget-dashboard .metric-card:hover {
|
|
4766
|
+
transform: translateY(-2px);
|
|
4767
|
+
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
|
4768
|
+
}
|
|
4769
|
+
|
|
4770
|
+
.metric-icon {
|
|
4771
|
+
font-size: 24px;
|
|
4772
|
+
margin-bottom: 10px;
|
|
4773
|
+
color: #6c757d;
|
|
4774
|
+
}
|
|
4775
|
+
|
|
4776
|
+
.metric-label {
|
|
4777
|
+
font-size: 14px;
|
|
4778
|
+
font-weight: 500;
|
|
4779
|
+
color: #6c757d;
|
|
4780
|
+
margin-bottom: 5px;
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4783
|
+
.metric-value {
|
|
4784
|
+
font-size: 32px;
|
|
4785
|
+
font-weight: 700;
|
|
4786
|
+
margin: 0;
|
|
4787
|
+
line-height: 1;
|
|
4788
|
+
}
|
|
4789
|
+
|
|
4790
|
+
.metric-trend {
|
|
4791
|
+
margin-top: 8px;
|
|
4792
|
+
font-size: 12px;
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
.metric-critical {
|
|
4796
|
+
border-left: 4px solid #dc3545;
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
.metric-warning {
|
|
4800
|
+
border-left: 4px solid #ffc107;
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
.panel-subtitle {
|
|
4804
|
+
position: absolute;
|
|
4805
|
+
right: 15px;
|
|
4806
|
+
top: 50%;
|
|
4807
|
+
transform: translateY(-50%);
|
|
4808
|
+
font-size: 12px;
|
|
4809
|
+
color: #6c757d;
|
|
4810
|
+
}
|
|
4811
|
+
`;
|
|
4812
|
+
}
|
|
4813
|
+
// Table-specific styles
|
|
4814
|
+
if (lower.includes('table') || lower.includes('list')) {
|
|
4815
|
+
css += `
|
|
4816
|
+
.widget-table .panel-actions {
|
|
4817
|
+
display: flex;
|
|
4818
|
+
gap: 10px;
|
|
4819
|
+
align-items: center;
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
.widget-table .panel-actions input {
|
|
4823
|
+
width: 200px;
|
|
4824
|
+
}
|
|
4825
|
+
|
|
4826
|
+
.widget-table .sortable {
|
|
4827
|
+
cursor: pointer;
|
|
4828
|
+
user-select: none;
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4831
|
+
.widget-table .sortable:hover {
|
|
4832
|
+
background-color: #f8f9fa;
|
|
4833
|
+
}
|
|
4834
|
+
|
|
4835
|
+
.widget-table .table > tbody > tr:hover {
|
|
4836
|
+
background-color: #f8f9fa;
|
|
4837
|
+
}
|
|
4838
|
+
`;
|
|
4839
|
+
}
|
|
4840
|
+
// Form-specific styles
|
|
4841
|
+
if (lower.includes('form') || lower.includes('input')) {
|
|
4842
|
+
css += `
|
|
4843
|
+
.widget-form .form-actions {
|
|
4844
|
+
margin-top: 20px;
|
|
4845
|
+
padding-top: 20px;
|
|
4846
|
+
border-top: 1px solid #dee2e6;
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
.widget-form .form-actions button {
|
|
4850
|
+
margin-right: 10px;
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
.widget-form .form-group {
|
|
4854
|
+
margin-bottom: 20px;
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
.widget-form .control-label {
|
|
4858
|
+
font-weight: 500;
|
|
4859
|
+
margin-bottom: 5px;
|
|
4860
|
+
}
|
|
4861
|
+
`;
|
|
4862
|
+
}
|
|
4863
|
+
return css;
|
|
4864
|
+
}
|
|
4865
|
+
/**
|
|
4866
|
+
* Generate server-side script based on widget requirements
|
|
4867
|
+
*/
|
|
4868
|
+
generateWidgetServerScript(instruction) {
|
|
4869
|
+
const lower = instruction.toLowerCase();
|
|
4870
|
+
const isChart = lower.includes('chart') || lower.includes('graph');
|
|
4871
|
+
const isDashboard = lower.includes('dashboard') || lower.includes('metrics');
|
|
4872
|
+
const isTable = lower.includes('table') || lower.includes('list');
|
|
4873
|
+
const isForm = lower.includes('form') || lower.includes('input');
|
|
4874
|
+
let script = `(function() {
|
|
4875
|
+
// Widget data initialization
|
|
4876
|
+
data.title = options.title || '${this.extractNameFromInstruction(instruction).replace(/_/g, ' ')}';
|
|
4877
|
+
data.widget_id = 'widget_' + Date.now();
|
|
4878
|
+
data.loading = false;
|
|
4879
|
+
data.error = null;
|
|
4880
|
+
|
|
4881
|
+
// Configuration options
|
|
4882
|
+
data.refresh_enabled = options.refresh_enabled || true;
|
|
4883
|
+
data.search_enabled = options.search_enabled || false;
|
|
4884
|
+
|
|
4885
|
+
`;
|
|
4886
|
+
if (isChart) {
|
|
4887
|
+
script += ` // Chart data initialization
|
|
4888
|
+
data.chart_type = options.chart_type || 'bar';
|
|
4889
|
+
data.chart_data = [];
|
|
4890
|
+
data.chart_labels = [];
|
|
4891
|
+
|
|
4892
|
+
// Sample chart data - replace with real data source
|
|
4893
|
+
try {
|
|
4894
|
+
var gr = new GlideRecord('incident');
|
|
4895
|
+
gr.addQuery('active', true);
|
|
4896
|
+
gr.addAggregate('COUNT');
|
|
4897
|
+
gr.groupBy('priority');
|
|
4898
|
+
gr.query();
|
|
4899
|
+
|
|
4900
|
+
data.chart_labels = [];
|
|
4901
|
+
data.chart_data = [];
|
|
4902
|
+
|
|
4903
|
+
while (gr.next()) {
|
|
4904
|
+
data.chart_labels.push('Priority ' + gr.priority.getDisplayValue());
|
|
4905
|
+
data.chart_data.push(parseInt(gr.getAggregate('COUNT')));
|
|
4906
|
+
}
|
|
4907
|
+
} catch (e) {
|
|
4908
|
+
data.error = 'Failed to load chart data: ' + e.getMessage();
|
|
4909
|
+
}
|
|
4910
|
+
`;
|
|
4911
|
+
}
|
|
4912
|
+
else if (isDashboard) {
|
|
4913
|
+
script += ` // Dashboard metrics initialization
|
|
4914
|
+
data.metrics = [];
|
|
4915
|
+
data.last_updated = new Date();
|
|
4916
|
+
|
|
4917
|
+
// Sample metrics - replace with real data sources
|
|
4918
|
+
try {
|
|
4919
|
+
// Incident metrics
|
|
4920
|
+
var incidentGr = new GlideRecord('incident');
|
|
4921
|
+
incidentGr.addQuery('active', true);
|
|
4922
|
+
incidentGr.query();
|
|
4923
|
+
var totalIncidents = incidentGr.getRowCount();
|
|
4924
|
+
|
|
4925
|
+
var criticalGr = new GlideRecord('incident');
|
|
4926
|
+
criticalGr.addQuery('active', true);
|
|
4927
|
+
criticalGr.addQuery('priority', '1');
|
|
4928
|
+
criticalGr.query();
|
|
4929
|
+
var criticalIncidents = criticalGr.getRowCount();
|
|
4930
|
+
|
|
4931
|
+
data.metrics = [
|
|
4932
|
+
{
|
|
4933
|
+
label: 'Active Incidents',
|
|
4934
|
+
value: totalIncidents,
|
|
4935
|
+
icon: 'exclamation-triangle',
|
|
4936
|
+
critical: totalIncidents > 100,
|
|
4937
|
+
warning: totalIncidents > 50
|
|
4938
|
+
},
|
|
4939
|
+
{
|
|
4940
|
+
label: 'Critical Issues',
|
|
4941
|
+
value: criticalIncidents,
|
|
4942
|
+
icon: 'fire',
|
|
4943
|
+
critical: criticalIncidents > 5,
|
|
4944
|
+
unit: 'critical'
|
|
4945
|
+
},
|
|
4946
|
+
{
|
|
4947
|
+
label: 'Resolution Rate',
|
|
4948
|
+
value: '94%',
|
|
4949
|
+
icon: 'check-circle',
|
|
4950
|
+
success: true,
|
|
4951
|
+
trend: 2,
|
|
4952
|
+
trend_text: '+2% this week'
|
|
4953
|
+
}
|
|
4954
|
+
];
|
|
4955
|
+
} catch (e) {
|
|
4956
|
+
data.error = 'Failed to load dashboard data: ' + e.getMessage();
|
|
4957
|
+
}
|
|
4958
|
+
`;
|
|
4959
|
+
}
|
|
4960
|
+
else if (isTable) {
|
|
4961
|
+
script += ` // Table data initialization
|
|
4962
|
+
data.records = [];
|
|
4963
|
+
data.columns = [];
|
|
4964
|
+
|
|
4965
|
+
// Define table columns - customize based on requirements
|
|
4966
|
+
data.columns = [
|
|
4967
|
+
{ field: 'number', label: 'Number', type: 'link' },
|
|
4968
|
+
{ field: 'short_description', label: 'Description' },
|
|
4969
|
+
{ field: 'priority', label: 'Priority' },
|
|
4970
|
+
{ field: 'state', label: 'State' },
|
|
4971
|
+
{ field: 'sys_created_on', label: 'Created', type: 'date' }
|
|
4972
|
+
];
|
|
4973
|
+
|
|
4974
|
+
// Load table data - replace with appropriate table/query
|
|
4975
|
+
try {
|
|
4976
|
+
var gr = new GlideRecord('incident');
|
|
4977
|
+
gr.addQuery('active', true);
|
|
4978
|
+
gr.orderByDesc('sys_created_on');
|
|
4979
|
+
gr.setLimit(50);
|
|
4980
|
+
gr.query();
|
|
4981
|
+
|
|
4982
|
+
while (gr.next()) {
|
|
4983
|
+
data.records.push({
|
|
4984
|
+
sys_id: gr.getUniqueValue(),
|
|
4985
|
+
number: gr.number.getDisplayValue(),
|
|
4986
|
+
number_link: '/' + gr.getTableName() + '.do?sys_id=' + gr.getUniqueValue(),
|
|
4987
|
+
short_description: gr.short_description.getDisplayValue(),
|
|
4988
|
+
priority: gr.priority.getDisplayValue(),
|
|
4989
|
+
state: gr.state.getDisplayValue(),
|
|
4990
|
+
sys_created_on: gr.sys_created_on.getDisplayValue()
|
|
4991
|
+
});
|
|
4992
|
+
}
|
|
4993
|
+
} catch (e) {
|
|
4994
|
+
data.error = 'Failed to load table data: ' + e.getMessage();
|
|
4995
|
+
}
|
|
4996
|
+
`;
|
|
4997
|
+
}
|
|
4998
|
+
else if (isForm) {
|
|
4999
|
+
script += ` // Form configuration
|
|
5000
|
+
data.fields = [];
|
|
5001
|
+
|
|
5002
|
+
// Define form fields - customize based on requirements
|
|
5003
|
+
data.fields = [
|
|
5004
|
+
{
|
|
5005
|
+
name: 'short_description',
|
|
5006
|
+
label: 'Short Description',
|
|
5007
|
+
type: 'text',
|
|
5008
|
+
required: true,
|
|
5009
|
+
placeholder: 'Enter a brief description'
|
|
5010
|
+
},
|
|
5011
|
+
{
|
|
5012
|
+
name: 'description',
|
|
5013
|
+
label: 'Detailed Description',
|
|
5014
|
+
type: 'textarea',
|
|
5015
|
+
required: false,
|
|
5016
|
+
placeholder: 'Provide additional details'
|
|
5017
|
+
},
|
|
5018
|
+
{
|
|
5019
|
+
name: 'priority',
|
|
5020
|
+
label: 'Priority',
|
|
5021
|
+
type: 'select',
|
|
5022
|
+
required: true,
|
|
5023
|
+
options: [
|
|
5024
|
+
{ value: '1', label: '1 - Critical' },
|
|
5025
|
+
{ value: '2', label: '2 - High' },
|
|
5026
|
+
{ value: '3', label: '3 - Moderate' },
|
|
5027
|
+
{ value: '4', label: '4 - Low' }
|
|
5028
|
+
]
|
|
5029
|
+
}
|
|
5030
|
+
];
|
|
5031
|
+
`;
|
|
5032
|
+
}
|
|
5033
|
+
else {
|
|
5034
|
+
script += ` // Generic data initialization
|
|
5035
|
+
data.items = [];
|
|
5036
|
+
data.empty_message = 'No items to display';
|
|
5037
|
+
data.empty_description = 'Configure this widget to show your data';
|
|
5038
|
+
|
|
5039
|
+
// Sample data - replace with real data source
|
|
5040
|
+
try {
|
|
5041
|
+
// Add your data loading logic here
|
|
5042
|
+
data.items = [
|
|
5043
|
+
{
|
|
5044
|
+
title: 'Sample Item 1',
|
|
5045
|
+
description: 'This is a sample item for demonstration',
|
|
5046
|
+
meta: 'Sample metadata'
|
|
5047
|
+
},
|
|
5048
|
+
{
|
|
5049
|
+
title: 'Sample Item 2',
|
|
5050
|
+
description: 'Another sample item',
|
|
5051
|
+
meta: 'More metadata'
|
|
5052
|
+
}
|
|
5053
|
+
];
|
|
5054
|
+
} catch (e) {
|
|
5055
|
+
data.error = 'Failed to load data: ' + e.getMessage();
|
|
5056
|
+
}
|
|
5057
|
+
`;
|
|
5058
|
+
}
|
|
5059
|
+
script += `})();`;
|
|
5060
|
+
return script;
|
|
5061
|
+
}
|
|
5062
|
+
/**
|
|
5063
|
+
* Generate client-side script based on widget requirements
|
|
5064
|
+
*/
|
|
5065
|
+
generateWidgetClientScript(instruction) {
|
|
5066
|
+
const lower = instruction.toLowerCase();
|
|
5067
|
+
const isChart = lower.includes('chart') || lower.includes('graph');
|
|
5068
|
+
const isDashboard = lower.includes('dashboard') || lower.includes('metrics');
|
|
5069
|
+
const isTable = lower.includes('table') || lower.includes('list');
|
|
5070
|
+
const isForm = lower.includes('form') || lower.includes('input');
|
|
5071
|
+
let script = `function($scope, $http) {
|
|
5072
|
+
var c = this;
|
|
5073
|
+
|
|
5074
|
+
// Initialize controller data
|
|
5075
|
+
c.loading = false;
|
|
5076
|
+
c.error = null;
|
|
5077
|
+
|
|
5078
|
+
`;
|
|
5079
|
+
if (isChart) {
|
|
5080
|
+
script += ` // Chart initialization
|
|
5081
|
+
c.chartInstance = null;
|
|
5082
|
+
|
|
5083
|
+
c.$onInit = function() {
|
|
5084
|
+
c.initializeChart();
|
|
5085
|
+
};
|
|
5086
|
+
|
|
5087
|
+
c.initializeChart = function() {
|
|
5088
|
+
if (!c.data.chart_data || c.data.chart_data.length === 0) return;
|
|
5089
|
+
|
|
5090
|
+
var ctx = document.getElementById('chart-' + c.data.widget_id);
|
|
5091
|
+
if (!ctx) return;
|
|
5092
|
+
|
|
5093
|
+
c.chartInstance = new Chart(ctx, {
|
|
5094
|
+
type: c.data.chart_type || 'bar',
|
|
5095
|
+
data: {
|
|
5096
|
+
labels: c.data.chart_labels,
|
|
5097
|
+
datasets: [{
|
|
5098
|
+
label: c.data.title,
|
|
5099
|
+
data: c.data.chart_data,
|
|
5100
|
+
backgroundColor: [
|
|
5101
|
+
'#FF6384', '#36A2EB', '#FFCE56',
|
|
5102
|
+
'#4BC0C0', '#9966FF', '#FF9F40'
|
|
5103
|
+
],
|
|
5104
|
+
borderWidth: 1
|
|
5105
|
+
}]
|
|
5106
|
+
},
|
|
5107
|
+
options: {
|
|
5108
|
+
responsive: true,
|
|
5109
|
+
plugins: {
|
|
5110
|
+
legend: {
|
|
5111
|
+
position: 'top',
|
|
5112
|
+
},
|
|
5113
|
+
title: {
|
|
5114
|
+
display: true,
|
|
5115
|
+
text: c.data.title
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
});
|
|
5120
|
+
};
|
|
5121
|
+
|
|
5122
|
+
c.refreshData = function() {
|
|
5123
|
+
c.loading = true;
|
|
5124
|
+
c.server.update().then(function() {
|
|
5125
|
+
c.loading = false;
|
|
5126
|
+
c.initializeChart();
|
|
5127
|
+
});
|
|
5128
|
+
};
|
|
5129
|
+
`;
|
|
5130
|
+
}
|
|
5131
|
+
else if (isDashboard) {
|
|
5132
|
+
script += ` // Dashboard functionality
|
|
5133
|
+
c.refreshData = function() {
|
|
5134
|
+
c.loading = true;
|
|
5135
|
+
c.server.update().then(function() {
|
|
5136
|
+
c.loading = false;
|
|
5137
|
+
});
|
|
5138
|
+
};
|
|
5139
|
+
|
|
5140
|
+
// Auto-refresh every 5 minutes
|
|
5141
|
+
setInterval(function() {
|
|
5142
|
+
if (c.data.refresh_enabled) {
|
|
5143
|
+
c.refreshData();
|
|
5144
|
+
}
|
|
5145
|
+
}, 300000);
|
|
5146
|
+
`;
|
|
5147
|
+
}
|
|
5148
|
+
else if (isTable) {
|
|
5149
|
+
script += ` // Table functionality
|
|
5150
|
+
c.searchTerm = '';
|
|
5151
|
+
c.sortField = '';
|
|
5152
|
+
c.sortReverse = false;
|
|
5153
|
+
|
|
5154
|
+
c.sortBy = function(field) {
|
|
5155
|
+
if (c.sortField === field) {
|
|
5156
|
+
c.sortReverse = !c.sortReverse;
|
|
5157
|
+
} else {
|
|
5158
|
+
c.sortField = field;
|
|
5159
|
+
c.sortReverse = false;
|
|
5160
|
+
}
|
|
5161
|
+
};
|
|
5162
|
+
|
|
5163
|
+
c.refresh = function() {
|
|
5164
|
+
c.loading = true;
|
|
5165
|
+
c.server.update().then(function() {
|
|
5166
|
+
c.loading = false;
|
|
5167
|
+
});
|
|
5168
|
+
};
|
|
5169
|
+
`;
|
|
5170
|
+
}
|
|
5171
|
+
else if (isForm) {
|
|
5172
|
+
script += ` // Form functionality
|
|
5173
|
+
c.formData = {};
|
|
5174
|
+
c.submitting = false;
|
|
5175
|
+
|
|
5176
|
+
c.submitForm = function() {
|
|
5177
|
+
if ($scope.widgetForm.$invalid) return;
|
|
5178
|
+
|
|
5179
|
+
c.submitting = true;
|
|
5180
|
+
|
|
5181
|
+
// Submit form data to server
|
|
5182
|
+
c.server.update({
|
|
5183
|
+
action: 'submit_form',
|
|
5184
|
+
formData: c.formData
|
|
5185
|
+
}).then(function(response) {
|
|
5186
|
+
c.submitting = false;
|
|
5187
|
+
if (response.success) {
|
|
5188
|
+
// Reset form on success
|
|
5189
|
+
c.resetForm();
|
|
5190
|
+
// Show success message
|
|
5191
|
+
alert('Form submitted successfully!');
|
|
5192
|
+
} else {
|
|
5193
|
+
alert('Error submitting form: ' + (response.error || 'Unknown error'));
|
|
5194
|
+
}
|
|
5195
|
+
}).catch(function(error) {
|
|
5196
|
+
c.submitting = false;
|
|
5197
|
+
alert('Error submitting form: ' + error.message);
|
|
5198
|
+
});
|
|
5199
|
+
};
|
|
5200
|
+
|
|
5201
|
+
c.resetForm = function() {
|
|
5202
|
+
c.formData = {};
|
|
5203
|
+
$scope.widgetForm.$setPristine();
|
|
5204
|
+
$scope.widgetForm.$setUntouched();
|
|
5205
|
+
};
|
|
5206
|
+
`;
|
|
5207
|
+
}
|
|
5208
|
+
else {
|
|
5209
|
+
script += ` // Generic widget functionality
|
|
5210
|
+
c.refresh = function() {
|
|
5211
|
+
c.loading = true;
|
|
5212
|
+
c.server.update().then(function() {
|
|
5213
|
+
c.loading = false;
|
|
5214
|
+
});
|
|
5215
|
+
};
|
|
5216
|
+
`;
|
|
5217
|
+
}
|
|
5218
|
+
script += `}`;
|
|
5219
|
+
return script;
|
|
5220
|
+
}
|
|
4408
5221
|
/**
|
|
4409
5222
|
* Attempt direct deployment with specific scope
|
|
4410
5223
|
*/
|