@memberjunction/ng-skip-chat 2.58.0 → 2.60.0

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.
@@ -1,4 +1,5 @@
1
1
  import { LogError } from '@memberjunction/core';
2
+ import { BaseSingleton } from '@memberjunction/global';
2
3
  /**
3
4
  * CDN URLs for external dependencies
4
5
  * These can be configured via environment variables in the future
@@ -21,6 +22,106 @@ const CDN_URLS = {
21
22
  // Utilities
22
23
  LODASH_JS: 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js'
23
24
  };
25
+ /**
26
+ * Global component registry service for managing reusable React components
27
+ * Extends BaseSingleton to ensure a truly global singleton instance across
28
+ * the entire application, even if this code is loaded multiple times.
29
+ */
30
+ export class GlobalComponentRegistry extends BaseSingleton {
31
+ components = new Map();
32
+ constructor() {
33
+ super(); // Call parent constructor to register in global store
34
+ }
35
+ /**
36
+ * Get the singleton instance
37
+ */
38
+ static get Instance() {
39
+ return super.getInstance();
40
+ }
41
+ /**
42
+ * Register a component with a simple key
43
+ */
44
+ register(key, component) {
45
+ this.components.set(key, { component });
46
+ }
47
+ /**
48
+ * Get a component by key
49
+ */
50
+ get(key) {
51
+ const entry = this.components.get(key);
52
+ return entry?.component;
53
+ }
54
+ /**
55
+ * Register a component with metadata for versioning and context
56
+ */
57
+ registerWithMetadata(name, context, version, component, description) {
58
+ const key = this.createKey(name, context, version);
59
+ this.components.set(key, {
60
+ component,
61
+ metadata: { context, version, description }
62
+ });
63
+ // Also register without version for backwards compatibility
64
+ const contextKey = `${name}_${context}`;
65
+ if (!this.components.has(contextKey)) {
66
+ this.register(contextKey, component);
67
+ }
68
+ }
69
+ /**
70
+ * Create a standardized key from component metadata
71
+ */
72
+ createKey(name, context, version) {
73
+ return `${name}_${context}_${version}`;
74
+ }
75
+ /**
76
+ * Get all registered component keys (useful for debugging)
77
+ */
78
+ getRegisteredKeys() {
79
+ return Array.from(this.components.keys());
80
+ }
81
+ /**
82
+ * Clear all registered components
83
+ */
84
+ clear() {
85
+ this.components.clear();
86
+ }
87
+ /**
88
+ * Check if a component is registered
89
+ */
90
+ has(key) {
91
+ return this.components.has(key);
92
+ }
93
+ /**
94
+ * Get component with fallback options
95
+ */
96
+ getWithFallback(name, context, version) {
97
+ // Try exact match first
98
+ let key = this.createKey(name, context, version);
99
+ if (this.has(key)) {
100
+ return this.get(key);
101
+ }
102
+ // Try without version
103
+ key = `${name}_${context}`;
104
+ if (this.has(key)) {
105
+ return this.get(key);
106
+ }
107
+ // Try global version
108
+ key = `${name}_Global`;
109
+ if (this.has(key)) {
110
+ return this.get(key);
111
+ }
112
+ // Try just the name
113
+ if (this.has(name)) {
114
+ return this.get(name);
115
+ }
116
+ return null;
117
+ }
118
+ /**
119
+ * Remove a component from the registry
120
+ */
121
+ remove(key) {
122
+ this.components.delete(key);
123
+ }
124
+ }
24
125
  /**
25
126
  * Default styles that match the Skip design system
26
127
  */
@@ -145,6 +246,30 @@ export class SkipReactComponentHost {
145
246
  this.config = config;
146
247
  this.loadReactLibraries();
147
248
  }
249
+ /**
250
+ * Create a plain JavaScript object containing only the components needed by the generated component
251
+ */
252
+ createComponentsObject() {
253
+ if (!this.config.metadata?.requiredChildComponents) {
254
+ return {}; // No child components required
255
+ }
256
+ const registry = GlobalComponentRegistry.Instance;
257
+ const components = {};
258
+ console.log('Creating components object. Required:', this.config.metadata.requiredChildComponents);
259
+ console.log('Available components in registry:', registry.getRegisteredKeys());
260
+ for (const componentName of this.config.metadata.requiredChildComponents) {
261
+ // Try to resolve the component with metadata context
262
+ const component = registry.getWithFallback(componentName, this.config.metadata.componentContext, this.config.metadata.version);
263
+ if (component) {
264
+ components[componentName] = component;
265
+ console.log(`Found component "${componentName}"`);
266
+ }
267
+ else {
268
+ console.warn(`Component "${componentName}" not found in registry. Tried contexts: ${this.config.metadata.componentContext}, Global`);
269
+ }
270
+ }
271
+ return components;
272
+ }
148
273
  /**
149
274
  * Load React and ReactDOM dynamically
150
275
  */
@@ -313,6 +438,16 @@ export class SkipReactComponentHost {
313
438
  this.loadBabel(),
314
439
  this.loadCommonLibraries()
315
440
  ]);
441
+ // Register example components if needed (for testing)
442
+ if (this.config.metadata?.requiredChildComponents?.length) {
443
+ // Check if we need to register example components
444
+ const registry = GlobalComponentRegistry.Instance;
445
+ const hasComponents = this.config.metadata.requiredChildComponents.every(name => registry.getWithFallback(name, this.config.metadata.componentContext, this.config.metadata.version));
446
+ if (!hasComponents && typeof window.registerExampleComponents === 'function') {
447
+ // Try to register example components
448
+ window.registerExampleComponents(this.React, libraries.Chart);
449
+ }
450
+ }
316
451
  // Create utility functions
317
452
  const createStateUpdater = this.createStateUpdaterFunction();
318
453
  const createStandardEventHandler = this.createStandardEventHandlerFunction();
@@ -470,12 +605,14 @@ export class SkipReactComponentHost {
470
605
  utilities: utilities,
471
606
  userState: this.currentState,
472
607
  callbacks: callbacks,
473
- styles: styles
608
+ styles: styles,
609
+ components: this.createComponentsObject() // Add the filtered components object
474
610
  };
475
611
  // Debug: Log the data being passed to the component
476
612
  console.log('=== SkipReactComponentHost: Rendering component ===');
477
613
  console.log('Data:', componentProps.data);
478
614
  console.log('User state:', componentProps.userState);
615
+ console.log('Components:', Object.keys(componentProps.components));
479
616
  if (componentProps.data?.data_item_0) {
480
617
  console.log('First entity:', componentProps.data.data_item_0[0]);
481
618
  console.log('Entity count:', componentProps.data.data_item_0.length);
@@ -665,4 +802,1089 @@ export class SkipReactComponentHost {
665
802
  };
666
803
  }
667
804
  }
805
+ /**
806
+ * Example child components for testing the component registry system
807
+ * These would normally be defined in separate files and imported
808
+ */
809
+ // Example SearchBox component
810
+ export const createSearchBoxComponent = (React) => {
811
+ return function SearchBox({ data, config, state, onEvent, styles, statePath }) {
812
+ const [searchValue, setSearchValue] = React.useState(state?.searchValue || '');
813
+ const handleSearch = (value) => {
814
+ setSearchValue(value);
815
+ if (onEvent) {
816
+ onEvent({
817
+ type: 'stateChanged',
818
+ payload: {
819
+ statePath: statePath,
820
+ newState: { searchValue: value }
821
+ }
822
+ });
823
+ }
824
+ };
825
+ return React.createElement('div', {
826
+ style: {
827
+ padding: styles?.spacing?.md || '16px',
828
+ backgroundColor: styles?.colors?.surface || '#f8f9fa',
829
+ borderRadius: styles?.borders?.radius?.md || '8px',
830
+ marginBottom: styles?.spacing?.md || '16px'
831
+ }
832
+ }, [
833
+ React.createElement('input', {
834
+ key: 'search-input',
835
+ type: 'text',
836
+ value: searchValue,
837
+ onChange: (e) => handleSearch(e.target.value),
838
+ placeholder: config?.placeholder || 'Search...',
839
+ style: {
840
+ width: '100%',
841
+ padding: styles?.spacing?.sm || '8px',
842
+ border: `1px solid ${styles?.colors?.border || '#dee2e6'}`,
843
+ borderRadius: styles?.borders?.radius?.sm || '4px',
844
+ fontSize: styles?.typography?.fontSize?.md || '14px',
845
+ fontFamily: styles?.typography?.fontFamily || 'inherit'
846
+ }
847
+ })
848
+ ]);
849
+ };
850
+ };
851
+ // Example OrderList component
852
+ export const createOrderListComponent = (React) => {
853
+ return function OrderList({ data, config, state, onEvent, styles, statePath }) {
854
+ const [sortBy, setSortBy] = React.useState(state?.sortBy || 'date');
855
+ const [currentPage, setCurrentPage] = React.useState(state?.currentPage || 1);
856
+ const orders = data || [];
857
+ const pageSize = config?.pageSize || 10;
858
+ const totalPages = Math.ceil(orders.length / pageSize);
859
+ const updateState = (newState) => {
860
+ if (onEvent) {
861
+ onEvent({
862
+ type: 'stateChanged',
863
+ payload: {
864
+ statePath: statePath,
865
+ newState: { ...state, ...newState }
866
+ }
867
+ });
868
+ }
869
+ };
870
+ const handleSort = (field) => {
871
+ setSortBy(field);
872
+ updateState({ sortBy: field });
873
+ };
874
+ const handlePageChange = (page) => {
875
+ setCurrentPage(page);
876
+ updateState({ currentPage: page });
877
+ };
878
+ // Simple pagination
879
+ const startIndex = (currentPage - 1) * pageSize;
880
+ const endIndex = startIndex + pageSize;
881
+ const displayedOrders = orders.slice(startIndex, endIndex);
882
+ return React.createElement('div', {
883
+ style: {
884
+ backgroundColor: styles?.colors?.background || '#ffffff',
885
+ border: `1px solid ${styles?.colors?.border || '#dee2e6'}`,
886
+ borderRadius: styles?.borders?.radius?.md || '8px',
887
+ padding: styles?.spacing?.lg || '24px'
888
+ }
889
+ }, [
890
+ // Header
891
+ React.createElement('div', {
892
+ key: 'header',
893
+ style: {
894
+ display: 'flex',
895
+ justifyContent: 'space-between',
896
+ alignItems: 'center',
897
+ marginBottom: styles?.spacing?.md || '16px'
898
+ }
899
+ }, [
900
+ React.createElement('h3', {
901
+ key: 'title',
902
+ style: {
903
+ margin: 0,
904
+ fontSize: styles?.typography?.fontSize?.lg || '16px',
905
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600'
906
+ }
907
+ }, 'Orders'),
908
+ config?.sortable && React.createElement('select', {
909
+ key: 'sort',
910
+ value: sortBy,
911
+ onChange: (e) => handleSort(e.target.value),
912
+ style: {
913
+ padding: styles?.spacing?.sm || '8px',
914
+ border: `1px solid ${styles?.colors?.border || '#dee2e6'}`,
915
+ borderRadius: styles?.borders?.radius?.sm || '4px'
916
+ }
917
+ }, [
918
+ React.createElement('option', { key: 'date', value: 'date' }, 'Sort by Date'),
919
+ React.createElement('option', { key: 'amount', value: 'amount' }, 'Sort by Amount'),
920
+ React.createElement('option', { key: 'status', value: 'status' }, 'Sort by Status')
921
+ ])
922
+ ]),
923
+ // List
924
+ React.createElement('div', {
925
+ key: 'list',
926
+ style: { marginBottom: styles?.spacing?.md || '16px' }
927
+ }, displayedOrders.length > 0 ? displayedOrders.map((order, index) => React.createElement('div', {
928
+ key: order.id || index,
929
+ style: {
930
+ padding: styles?.spacing?.md || '16px',
931
+ borderBottom: `1px solid ${styles?.colors?.borderLight || '#f1f5f9'}`,
932
+ cursor: 'pointer'
933
+ },
934
+ onClick: () => onEvent && onEvent({
935
+ type: 'navigate',
936
+ payload: { entityName: 'Orders', key: order.id }
937
+ })
938
+ }, [
939
+ React.createElement('div', {
940
+ key: 'order-number',
941
+ style: { fontWeight: styles?.typography?.fontWeight?.medium || '500' }
942
+ }, `Order #${order.orderNumber || order.id}`),
943
+ React.createElement('div', {
944
+ key: 'order-details',
945
+ style: {
946
+ fontSize: styles?.typography?.fontSize?.sm || '12px',
947
+ color: styles?.colors?.textSecondary || '#6c757d'
948
+ }
949
+ }, `$${order.amount || 0} - ${order.status || 'Pending'}`)
950
+ ])) : React.createElement('div', {
951
+ style: {
952
+ textAlign: 'center',
953
+ padding: styles?.spacing?.xl || '32px',
954
+ color: styles?.colors?.textSecondary || '#6c757d'
955
+ }
956
+ }, 'No orders found')),
957
+ // Pagination
958
+ totalPages > 1 && React.createElement('div', {
959
+ key: 'pagination',
960
+ style: {
961
+ display: 'flex',
962
+ justifyContent: 'center',
963
+ gap: styles?.spacing?.sm || '8px'
964
+ }
965
+ }, Array.from({ length: totalPages }, (_, i) => i + 1).map(page => React.createElement('button', {
966
+ key: page,
967
+ onClick: () => handlePageChange(page),
968
+ style: {
969
+ padding: `${styles?.spacing?.xs || '4px'} ${styles?.spacing?.sm || '8px'}`,
970
+ border: `1px solid ${styles?.colors?.border || '#dee2e6'}`,
971
+ borderRadius: styles?.borders?.radius?.sm || '4px',
972
+ backgroundColor: page === currentPage ? styles?.colors?.primary || '#5B4FE9' : 'transparent',
973
+ color: page === currentPage ? styles?.colors?.textInverse || '#ffffff' : styles?.colors?.text || '#212529',
974
+ cursor: 'pointer'
975
+ }
976
+ }, page)))
977
+ ]);
978
+ };
979
+ };
980
+ // Example CategoryChart component
981
+ export const createCategoryChartComponent = (React, Chart) => {
982
+ return function CategoryChart({ data, config, state, onEvent, styles, statePath }) {
983
+ const chartRef = React.useRef(null);
984
+ const chartInstanceRef = React.useRef(null);
985
+ React.useEffect(() => {
986
+ if (!chartRef.current || !Chart)
987
+ return;
988
+ // Destroy existing chart
989
+ if (chartInstanceRef.current) {
990
+ chartInstanceRef.current.destroy();
991
+ }
992
+ // Create new chart
993
+ const ctx = chartRef.current.getContext('2d');
994
+ chartInstanceRef.current = new Chart(ctx, {
995
+ type: 'bar',
996
+ data: {
997
+ labels: data?.map((item) => item.category) || [],
998
+ datasets: [{
999
+ label: 'Sales by Category',
1000
+ data: data?.map((item) => item.value) || [],
1001
+ backgroundColor: styles?.colors?.primary || '#5B4FE9',
1002
+ borderColor: styles?.colors?.primaryHover || '#4940D4',
1003
+ borderWidth: 1
1004
+ }]
1005
+ },
1006
+ options: {
1007
+ responsive: true,
1008
+ maintainAspectRatio: false,
1009
+ plugins: {
1010
+ legend: {
1011
+ display: config?.showLegend !== false
1012
+ },
1013
+ tooltip: {
1014
+ callbacks: {
1015
+ label: (context) => {
1016
+ return `$${context.parsed.y.toLocaleString()}`;
1017
+ }
1018
+ }
1019
+ }
1020
+ },
1021
+ scales: {
1022
+ y: {
1023
+ beginAtZero: true,
1024
+ ticks: {
1025
+ callback: (value) => `$${value.toLocaleString()}`
1026
+ }
1027
+ }
1028
+ },
1029
+ onClick: (event, elements) => {
1030
+ if (elements.length > 0 && onEvent) {
1031
+ const index = elements[0].index;
1032
+ const category = data[index];
1033
+ onEvent({
1034
+ type: 'chartClick',
1035
+ payload: { category: category.category, value: category.value }
1036
+ });
1037
+ }
1038
+ }
1039
+ }
1040
+ });
1041
+ return () => {
1042
+ if (chartInstanceRef.current) {
1043
+ chartInstanceRef.current.destroy();
1044
+ }
1045
+ };
1046
+ }, [data, config, styles]);
1047
+ return React.createElement('div', {
1048
+ style: {
1049
+ backgroundColor: styles?.colors?.background || '#ffffff',
1050
+ border: `1px solid ${styles?.colors?.border || '#dee2e6'}`,
1051
+ borderRadius: styles?.borders?.radius?.md || '8px',
1052
+ padding: styles?.spacing?.lg || '24px',
1053
+ height: '400px'
1054
+ }
1055
+ }, [
1056
+ React.createElement('h3', {
1057
+ key: 'title',
1058
+ style: {
1059
+ margin: `0 0 ${styles?.spacing?.md || '16px'} 0`,
1060
+ fontSize: styles?.typography?.fontSize?.lg || '16px',
1061
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600'
1062
+ }
1063
+ }, 'Sales by Category'),
1064
+ React.createElement('canvas', {
1065
+ key: 'chart',
1066
+ ref: chartRef,
1067
+ style: { maxHeight: '320px' }
1068
+ })
1069
+ ]);
1070
+ };
1071
+ };
1072
+ // Example ActionCategoryList component string - simulates AI-generated code
1073
+ export const getActionCategoryListComponentString = () => {
1074
+ return String.raw `
1075
+ function createComponent(React, ReactDOM, useState, useEffect, useCallback, createStateUpdater, createStandardEventHandler) {
1076
+ function ActionCategoryList({ data, config, state, onEvent, styles, statePath, utilities, selectedCategoryID }) {
1077
+ const [categories, setCategories] = useState([]);
1078
+ const [expandedCategories, setExpandedCategories] = useState(new Set());
1079
+ const [loading, setLoading] = useState(true);
1080
+ const [error, setError] = useState(null);
1081
+
1082
+ useEffect(() => {
1083
+ loadCategories();
1084
+ }, []);
1085
+
1086
+ const loadCategories = async () => {
1087
+ if (!utilities?.rv) {
1088
+ setError('RunView utility not available');
1089
+ setLoading(false);
1090
+ return;
1091
+ }
1092
+
1093
+ try {
1094
+ setLoading(true);
1095
+ setError(null);
1096
+
1097
+ const result = await utilities.rv.RunView({
1098
+ EntityName: 'Action Categories',
1099
+ ExtraFilter: '',
1100
+ OrderBy: 'Name',
1101
+ MaxRows: 1000,
1102
+ ResultType: 'entity_object'
1103
+ });
1104
+
1105
+ if (result.Success && result.Results) {
1106
+ setCategories(result.Results);
1107
+ } else {
1108
+ setError(result.ErrorMessage || 'Failed to load categories');
1109
+ }
1110
+ } catch (err) {
1111
+ setError('Error loading categories: ' + err);
1112
+ } finally {
1113
+ setLoading(false);
1114
+ }
1115
+ };
1116
+
1117
+ const handleCategoryClick = (category) => {
1118
+ if (onEvent) {
1119
+ onEvent({
1120
+ type: 'categorySelected',
1121
+ source: 'ActionCategoryList',
1122
+ payload: {
1123
+ categoryID: category.ID,
1124
+ categoryName: category.Name
1125
+ }
1126
+ });
1127
+ }
1128
+ };
1129
+
1130
+ if (loading) {
1131
+ return React.createElement('div', {
1132
+ style: {
1133
+ padding: styles?.spacing?.lg || '24px',
1134
+ textAlign: 'center',
1135
+ color: styles?.colors?.textSecondary || '#6c757d'
1136
+ }
1137
+ }, 'Loading categories...');
1138
+ }
1139
+
1140
+ if (error) {
1141
+ return React.createElement('div', {
1142
+ style: {
1143
+ padding: styles?.spacing?.lg || '24px',
1144
+ color: styles?.colors?.error || '#dc3545'
1145
+ }
1146
+ }, error);
1147
+ }
1148
+
1149
+ return React.createElement('div', {
1150
+ style: {
1151
+ height: '100%',
1152
+ overflow: 'auto'
1153
+ }
1154
+ }, [
1155
+ React.createElement('h3', {
1156
+ key: 'title',
1157
+ style: {
1158
+ margin: '0 0 ' + (styles?.spacing?.md || '16px') + ' 0',
1159
+ padding: '0 ' + (styles?.spacing?.md || '16px'),
1160
+ fontSize: styles?.typography?.fontSize?.lg || '16px',
1161
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600'
1162
+ }
1163
+ }, 'Action Categories'),
1164
+
1165
+ React.createElement('div', {
1166
+ key: 'list',
1167
+ style: {
1168
+ display: 'flex',
1169
+ flexDirection: 'column',
1170
+ gap: styles?.spacing?.xs || '4px'
1171
+ }
1172
+ }, categories.map((category) =>
1173
+ React.createElement('div', {
1174
+ key: category.ID,
1175
+ onClick: () => handleCategoryClick(category),
1176
+ style: {
1177
+ padding: styles?.spacing?.md || '16px',
1178
+ cursor: 'pointer',
1179
+ backgroundColor: selectedCategoryID === category.ID
1180
+ ? styles?.colors?.primaryLight || '#e8e6ff'
1181
+ : 'transparent',
1182
+ borderLeft: selectedCategoryID === category.ID
1183
+ ? '3px solid ' + (styles?.colors?.primary || '#5B4FE9')
1184
+ : '3px solid transparent',
1185
+ transition: styles?.transitions?.fast || '150ms ease-in-out'
1186
+ },
1187
+ onMouseEnter: (e) => {
1188
+ if (selectedCategoryID !== category.ID) {
1189
+ e.currentTarget.style.backgroundColor = styles?.colors?.surfaceHover || '#f1f5f9';
1190
+ }
1191
+ },
1192
+ onMouseLeave: (e) => {
1193
+ if (selectedCategoryID !== category.ID) {
1194
+ e.currentTarget.style.backgroundColor = 'transparent';
1195
+ }
1196
+ }
1197
+ }, [
1198
+ React.createElement('div', {
1199
+ key: 'name',
1200
+ style: {
1201
+ fontSize: styles?.typography?.fontSize?.md || '14px',
1202
+ fontWeight: selectedCategoryID === category.ID
1203
+ ? styles?.typography?.fontWeight?.medium || '500'
1204
+ : styles?.typography?.fontWeight?.regular || '400',
1205
+ color: styles?.colors?.text || '#212529',
1206
+ marginBottom: styles?.spacing?.xs || '4px'
1207
+ }
1208
+ }, category.Name),
1209
+
1210
+ category.Description && React.createElement('div', {
1211
+ key: 'description',
1212
+ style: {
1213
+ fontSize: styles?.typography?.fontSize?.sm || '12px',
1214
+ color: styles?.colors?.textSecondary || '#6c757d',
1215
+ lineHeight: styles?.typography?.lineHeight?.normal || '1.5'
1216
+ }
1217
+ }, category.Description)
1218
+ ])
1219
+ ))
1220
+ ]);
1221
+ }
1222
+
1223
+ return { component: ActionCategoryList };
1224
+ }
1225
+ `;
1226
+ };
1227
+ // Example ActionList component string - simulates AI-generated code
1228
+ export const getActionListComponentString = () => {
1229
+ return String.raw `
1230
+ function createComponent(React, ReactDOM, useState, useEffect, useCallback, createStateUpdater, createStandardEventHandler) {
1231
+ function ActionList({ data, config, state, onEvent, styles, statePath, utilities, selectedCategoryID }) {
1232
+ const [actions, setActions] = useState([]);
1233
+ const [expandedActions, setExpandedActions] = useState(new Set());
1234
+ const [actionDetails, setActionDetails] = useState({});
1235
+ const [loading, setLoading] = useState(false);
1236
+ const [error, setError] = useState(null);
1237
+
1238
+ useEffect(() => {
1239
+ if (selectedCategoryID) {
1240
+ loadActions(selectedCategoryID);
1241
+ } else {
1242
+ setActions([]);
1243
+ }
1244
+ }, [selectedCategoryID]);
1245
+
1246
+ const loadActions = async (categoryID) => {
1247
+ if (!utilities?.rv) {
1248
+ setError('RunView utility not available');
1249
+ return;
1250
+ }
1251
+
1252
+ try {
1253
+ setLoading(true);
1254
+ setError(null);
1255
+
1256
+ const result = await utilities.rv.RunView({
1257
+ EntityName: 'Actions',
1258
+ ExtraFilter: 'CategoryID = \'' + categoryID + '\'',
1259
+ OrderBy: 'Name',
1260
+ MaxRows: 1000,
1261
+ ResultType: 'entity_object'
1262
+ });
1263
+
1264
+ if (result.Success && result.Results) {
1265
+ setActions(result.Results);
1266
+ } else {
1267
+ setError(result.ErrorMessage || 'Failed to load actions');
1268
+ }
1269
+ } catch (err) {
1270
+ setError('Error loading actions: ' + err);
1271
+ } finally {
1272
+ setLoading(false);
1273
+ }
1274
+ };
1275
+
1276
+ const handleActionClick = async (action) => {
1277
+ // Toggle expanded state
1278
+ const newExpanded = new Set(expandedActions);
1279
+ if (newExpanded.has(action.ID)) {
1280
+ newExpanded.delete(action.ID);
1281
+ } else {
1282
+ newExpanded.add(action.ID);
1283
+ // Load details if not already loaded
1284
+ if (!actionDetails[action.ID]) {
1285
+ await loadActionDetails(action.ID);
1286
+ }
1287
+ }
1288
+ setExpandedActions(newExpanded);
1289
+
1290
+ if (onEvent) {
1291
+ onEvent({
1292
+ type: 'actionSelected',
1293
+ source: 'ActionList',
1294
+ payload: {
1295
+ actionID: action.ID,
1296
+ actionName: action.Name
1297
+ }
1298
+ });
1299
+ }
1300
+ };
1301
+
1302
+ const loadActionDetails = async (actionID) => {
1303
+ if (!utilities?.rv) return;
1304
+
1305
+ try {
1306
+ // Load params and result codes in parallel
1307
+ const [paramsResult, resultCodesResult] = await Promise.all([
1308
+ utilities.rv.RunView({
1309
+ EntityName: 'Action Params',
1310
+ ExtraFilter: 'ActionID = \'' + actionID + '\'',
1311
+ OrderBy: 'Name',
1312
+ ResultType: 'entity_object'
1313
+ }),
1314
+ utilities.rv.RunView({
1315
+ EntityName: 'Action Result Codes',
1316
+ ExtraFilter: 'ActionID = \'' + actionID + '\'',
1317
+ OrderBy: 'ResultCode',
1318
+ ResultType: 'entity_object'
1319
+ })
1320
+ ]);
1321
+
1322
+ const details = {
1323
+ params: paramsResult.Success ? paramsResult.Results : [],
1324
+ resultCodes: resultCodesResult.Success ? resultCodesResult.Results : []
1325
+ };
1326
+
1327
+ setActionDetails(prev => ({ ...prev, [actionID]: details }));
1328
+ } catch (err) {
1329
+ console.error('Error loading action details:', err);
1330
+ }
1331
+ };
1332
+
1333
+ if (!selectedCategoryID) {
1334
+ return React.createElement('div', {
1335
+ style: {
1336
+ padding: styles?.spacing?.xl || '32px',
1337
+ textAlign: 'center',
1338
+ color: styles?.colors?.textSecondary || '#6c757d'
1339
+ }
1340
+ }, 'Select a category to view actions');
1341
+ }
1342
+
1343
+ if (loading) {
1344
+ return React.createElement('div', {
1345
+ style: {
1346
+ padding: styles?.spacing?.xl || '32px',
1347
+ textAlign: 'center',
1348
+ color: styles?.colors?.textSecondary || '#6c757d'
1349
+ }
1350
+ }, 'Loading actions...');
1351
+ }
1352
+
1353
+ if (error) {
1354
+ return React.createElement('div', {
1355
+ style: {
1356
+ padding: styles?.spacing?.lg || '24px',
1357
+ color: styles?.colors?.error || '#dc3545'
1358
+ }
1359
+ }, error);
1360
+ }
1361
+
1362
+ if (actions.length === 0) {
1363
+ return React.createElement('div', {
1364
+ style: {
1365
+ padding: styles?.spacing?.xl || '32px',
1366
+ textAlign: 'center',
1367
+ color: styles?.colors?.textSecondary || '#6c757d'
1368
+ }
1369
+ }, 'No actions found in this category');
1370
+ }
1371
+
1372
+ return React.createElement('div', {
1373
+ style: {
1374
+ padding: styles?.spacing?.lg || '24px'
1375
+ }
1376
+ }, [
1377
+ React.createElement('h3', {
1378
+ key: 'title',
1379
+ style: {
1380
+ margin: '0 0 ' + (styles?.spacing?.lg || '24px') + ' 0',
1381
+ fontSize: styles?.typography?.fontSize?.lg || '16px',
1382
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600'
1383
+ }
1384
+ }, 'Actions (' + actions.length + ')'),
1385
+
1386
+ React.createElement('div', {
1387
+ key: 'grid',
1388
+ style: {
1389
+ display: 'grid',
1390
+ gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
1391
+ gap: styles?.spacing?.md || '16px'
1392
+ }
1393
+ }, actions.map((action) => {
1394
+ const isExpanded = expandedActions.has(action.ID);
1395
+ const details = actionDetails[action.ID] || { params: [], resultCodes: [] };
1396
+
1397
+ return React.createElement('div', {
1398
+ key: action.ID,
1399
+ style: {
1400
+ backgroundColor: styles?.colors?.surface || '#f8f9fa',
1401
+ border: '1px solid ' + (styles?.colors?.border || '#dee2e6'),
1402
+ borderRadius: styles?.borders?.radius?.md || '8px',
1403
+ overflow: 'hidden',
1404
+ transition: styles?.transitions?.fast || '150ms ease-in-out'
1405
+ }
1406
+ }, [
1407
+ React.createElement('div', {
1408
+ key: 'header',
1409
+ onClick: () => handleActionClick(action),
1410
+ style: {
1411
+ padding: styles?.spacing?.md || '16px',
1412
+ cursor: 'pointer'
1413
+ },
1414
+ onMouseEnter: (e) => {
1415
+ e.currentTarget.style.backgroundColor = styles?.colors?.surfaceHover || '#f1f5f9';
1416
+ },
1417
+ onMouseLeave: (e) => {
1418
+ e.currentTarget.style.backgroundColor = 'transparent';
1419
+ }
1420
+ }, [
1421
+ React.createElement('div', {
1422
+ key: 'header-content',
1423
+ style: {
1424
+ display: 'flex',
1425
+ alignItems: 'center',
1426
+ justifyContent: 'space-between'
1427
+ }
1428
+ }, [
1429
+ React.createElement('div', { key: 'main-content' }, [
1430
+ React.createElement('div', {
1431
+ key: 'name',
1432
+ style: {
1433
+ fontSize: styles?.typography?.fontSize?.md || '14px',
1434
+ fontWeight: styles?.typography?.fontWeight?.medium || '500',
1435
+ color: styles?.colors?.text || '#212529',
1436
+ marginBottom: styles?.spacing?.xs || '4px'
1437
+ }
1438
+ }, action.Name),
1439
+
1440
+ action.Description && React.createElement('div', {
1441
+ key: 'description',
1442
+ style: {
1443
+ fontSize: styles?.typography?.fontSize?.sm || '12px',
1444
+ color: styles?.colors?.textSecondary || '#6c757d',
1445
+ lineHeight: styles?.typography?.lineHeight?.normal || '1.5',
1446
+ marginBottom: styles?.spacing?.xs || '4px'
1447
+ }
1448
+ }, action.Description),
1449
+
1450
+ React.createElement('div', {
1451
+ key: 'metadata',
1452
+ style: {
1453
+ display: 'flex',
1454
+ gap: styles?.spacing?.md || '16px',
1455
+ fontSize: styles?.typography?.fontSize?.xs || '11px',
1456
+ color: styles?.colors?.textTertiary || '#94a3b8'
1457
+ }
1458
+ }, [
1459
+ action.Type && React.createElement('span', { key: 'type' }, 'Type: ' + action.Type),
1460
+ action.Status && React.createElement('span', { key: 'status' }, 'Status: ' + action.Status)
1461
+ ])
1462
+ ]),
1463
+
1464
+ React.createElement('span', {
1465
+ key: 'expand-icon',
1466
+ style: {
1467
+ fontSize: '12px',
1468
+ color: styles?.colors?.textSecondary || '#6c757d',
1469
+ marginLeft: '8px'
1470
+ }
1471
+ }, isExpanded ? '▼' : '▶')
1472
+ ])
1473
+ ]),
1474
+
1475
+ isExpanded && React.createElement('div', {
1476
+ key: 'details',
1477
+ style: {
1478
+ borderTop: '1px solid ' + (styles?.colors?.border || '#dee2e6'),
1479
+ backgroundColor: styles?.colors?.background || '#ffffff'
1480
+ }
1481
+ }, [
1482
+ // Parameters section
1483
+ details.params.length > 0 && React.createElement('div', {
1484
+ key: 'params',
1485
+ style: {
1486
+ padding: styles?.spacing?.md || '16px',
1487
+ borderBottom: '1px solid ' + (styles?.colors?.borderLight || '#f1f5f9')
1488
+ }
1489
+ }, [
1490
+ React.createElement('h4', {
1491
+ key: 'params-title',
1492
+ style: {
1493
+ margin: '0 0 ' + (styles?.spacing?.sm || '8px') + ' 0',
1494
+ fontSize: styles?.typography?.fontSize?.sm || '13px',
1495
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600',
1496
+ color: styles?.colors?.text || '#212529'
1497
+ }
1498
+ }, 'Parameters'),
1499
+
1500
+ React.createElement('div', {
1501
+ key: 'params-list',
1502
+ style: {
1503
+ display: 'flex',
1504
+ flexDirection: 'column',
1505
+ gap: styles?.spacing?.xs || '4px'
1506
+ }
1507
+ }, details.params.map(param =>
1508
+ React.createElement('div', {
1509
+ key: param.ID,
1510
+ style: {
1511
+ display: 'flex',
1512
+ alignItems: 'center',
1513
+ fontSize: styles?.typography?.fontSize?.xs || '12px',
1514
+ padding: '4px 0'
1515
+ }
1516
+ }, [
1517
+ React.createElement('span', {
1518
+ key: 'name',
1519
+ style: {
1520
+ fontWeight: styles?.typography?.fontWeight?.medium || '500',
1521
+ color: styles?.colors?.text || '#212529',
1522
+ marginRight: '8px'
1523
+ }
1524
+ }, param.Name),
1525
+
1526
+ React.createElement('span', {
1527
+ key: 'type',
1528
+ style: {
1529
+ color: styles?.colors?.textSecondary || '#6c757d',
1530
+ fontSize: '11px',
1531
+ backgroundColor: styles?.colors?.surfaceHover || '#f1f5f9',
1532
+ padding: '2px 6px',
1533
+ borderRadius: '3px',
1534
+ marginRight: '8px'
1535
+ }
1536
+ }, param.Type),
1537
+
1538
+ param.IsRequired && React.createElement('span', {
1539
+ key: 'required',
1540
+ style: {
1541
+ color: styles?.colors?.error || '#dc3545',
1542
+ fontSize: '10px',
1543
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600'
1544
+ }
1545
+ }, 'REQUIRED'),
1546
+
1547
+ param.Description && React.createElement('span', {
1548
+ key: 'desc',
1549
+ style: {
1550
+ color: styles?.colors?.textTertiary || '#94a3b8',
1551
+ marginLeft: 'auto',
1552
+ fontSize: '11px'
1553
+ }
1554
+ }, param.Description)
1555
+ ])
1556
+ ))
1557
+ ]),
1558
+
1559
+ // Result codes section
1560
+ details.resultCodes.length > 0 && React.createElement('div', {
1561
+ key: 'result-codes',
1562
+ style: {
1563
+ padding: styles?.spacing?.md || '16px'
1564
+ }
1565
+ }, [
1566
+ React.createElement('h4', {
1567
+ key: 'codes-title',
1568
+ style: {
1569
+ margin: '0 0 ' + (styles?.spacing?.sm || '8px') + ' 0',
1570
+ fontSize: styles?.typography?.fontSize?.sm || '13px',
1571
+ fontWeight: styles?.typography?.fontWeight?.semibold || '600',
1572
+ color: styles?.colors?.text || '#212529'
1573
+ }
1574
+ }, 'Result Codes'),
1575
+
1576
+ React.createElement('div', {
1577
+ key: 'codes-list',
1578
+ style: {
1579
+ display: 'flex',
1580
+ flexDirection: 'column',
1581
+ gap: styles?.spacing?.xs || '4px'
1582
+ }
1583
+ }, details.resultCodes.map(code =>
1584
+ React.createElement('div', {
1585
+ key: code.ID,
1586
+ style: {
1587
+ display: 'flex',
1588
+ alignItems: 'center',
1589
+ fontSize: styles?.typography?.fontSize?.xs || '12px',
1590
+ padding: '4px 0'
1591
+ }
1592
+ }, [
1593
+ React.createElement('span', {
1594
+ key: 'code',
1595
+ style: {
1596
+ fontFamily: 'monospace',
1597
+ fontWeight: styles?.typography?.fontWeight?.medium || '500',
1598
+ color: code.IsSuccess ? styles?.colors?.success || '#10b981' : styles?.colors?.error || '#dc3545',
1599
+ marginRight: '8px'
1600
+ }
1601
+ }, code.ResultCode),
1602
+
1603
+ code.Description && React.createElement('span', {
1604
+ key: 'desc',
1605
+ style: {
1606
+ color: styles?.colors?.textSecondary || '#6c757d'
1607
+ }
1608
+ }, code.Description)
1609
+ ])
1610
+ ))
1611
+ ])
1612
+ ])
1613
+ ]);
1614
+ }))
1615
+ ]);
1616
+ }
1617
+
1618
+ return { component: ActionList };
1619
+ }
1620
+ `;
1621
+ };
1622
+ // Example composite ActionBrowser component string - simulates AI-generated code
1623
+ export const getActionBrowserComponentString = () => {
1624
+ return String.raw `
1625
+ function createComponent(React, ReactDOM, useState, useEffect, useCallback, createStateUpdater, createStandardEventHandler) {
1626
+ function ActionBrowser({ data, utilities, userState, callbacks, styles, components }) {
1627
+ const [fullUserState, setFullUserState] = useState({
1628
+ selectedCategoryID: null,
1629
+ selectedActionID: null,
1630
+ categoryList: {},
1631
+ actionList: {},
1632
+ ...userState
1633
+ });
1634
+
1635
+ // Destructure child components from registry
1636
+ const { ActionCategoryList, ActionList } = components;
1637
+
1638
+ const updateUserState = (stateUpdate) => {
1639
+ const newState = { ...fullUserState, ...stateUpdate };
1640
+ setFullUserState(newState);
1641
+ if (callbacks?.UpdateUserState) {
1642
+ callbacks.UpdateUserState(newState);
1643
+ }
1644
+ };
1645
+
1646
+ const handleComponentEvent = (event) => {
1647
+ if (event.type === 'categorySelected' && event.source === 'ActionCategoryList') {
1648
+ updateUserState({
1649
+ selectedCategoryID: event.payload.categoryID,
1650
+ selectedActionID: null // Reset action selection
1651
+ });
1652
+ return;
1653
+ }
1654
+
1655
+ if (event.type === 'actionSelected' && event.source === 'ActionList') {
1656
+ updateUserState({
1657
+ selectedActionID: event.payload.actionID
1658
+ });
1659
+ if (callbacks?.NotifyEvent) {
1660
+ callbacks.NotifyEvent('actionSelected', event.payload);
1661
+ }
1662
+ return;
1663
+ }
1664
+
1665
+ // Handle standard state changes
1666
+ if (event.type === 'stateChanged') {
1667
+ const update = {};
1668
+ update[event.payload.statePath] = event.payload.newState;
1669
+ updateUserState(update);
1670
+ }
1671
+ };
1672
+
1673
+ return React.createElement('div', {
1674
+ style: {
1675
+ display: 'flex',
1676
+ height: '100%',
1677
+ minHeight: '600px',
1678
+ backgroundColor: styles.colors.background,
1679
+ fontFamily: styles.typography.fontFamily
1680
+ }
1681
+ }, [
1682
+ // Left sidebar with categories
1683
+ React.createElement('div', {
1684
+ key: 'sidebar',
1685
+ style: {
1686
+ width: '300px',
1687
+ backgroundColor: styles.colors.surface,
1688
+ borderRight: '1px solid ' + styles.colors.border,
1689
+ overflow: 'hidden',
1690
+ display: 'flex',
1691
+ flexDirection: 'column'
1692
+ }
1693
+ }, [
1694
+ ActionCategoryList && React.createElement(ActionCategoryList, {
1695
+ key: 'categories',
1696
+ data: [],
1697
+ config: {},
1698
+ state: fullUserState.categoryList || {},
1699
+ onEvent: handleComponentEvent,
1700
+ styles: styles,
1701
+ utilities: utilities,
1702
+ statePath: 'categoryList',
1703
+ selectedCategoryID: fullUserState.selectedCategoryID
1704
+ })
1705
+ ]),
1706
+
1707
+ // Main content area with actions
1708
+ React.createElement('div', {
1709
+ key: 'main',
1710
+ style: {
1711
+ flex: 1,
1712
+ overflow: 'auto'
1713
+ }
1714
+ }, [
1715
+ ActionList && React.createElement(ActionList, {
1716
+ key: 'actions',
1717
+ data: [],
1718
+ config: {},
1719
+ state: fullUserState.actionList || {},
1720
+ onEvent: handleComponentEvent,
1721
+ styles: styles,
1722
+ utilities: utilities,
1723
+ statePath: 'actionList',
1724
+ selectedCategoryID: fullUserState.selectedCategoryID
1725
+ })
1726
+ ])
1727
+ ]);
1728
+ }
1729
+
1730
+ return { component: ActionBrowser };
1731
+ }
1732
+ `;
1733
+ };
1734
+ /**
1735
+ * Unit tests for GlobalComponentRegistry
1736
+ * These would normally be in a separate .spec.ts file
1737
+ * Run these tests to ensure the registry works correctly
1738
+ */
1739
+ export function testGlobalComponentRegistry() {
1740
+ const registry = GlobalComponentRegistry.Instance;
1741
+ const testResults = [];
1742
+ const assert = (condition, testName, error) => {
1743
+ testResults.push({ test: testName, passed: condition, error: condition ? undefined : error });
1744
+ if (!condition) {
1745
+ console.error(`Test failed: ${testName}`, error);
1746
+ }
1747
+ else {
1748
+ console.log(`Test passed: ${testName}`);
1749
+ }
1750
+ };
1751
+ // Test 1: Singleton pattern
1752
+ const registry2 = GlobalComponentRegistry.Instance;
1753
+ assert(registry === registry2, 'Singleton pattern', 'Multiple instances created');
1754
+ // Test 2: Basic registration and retrieval
1755
+ registry.clear(); // Start fresh
1756
+ const mockComponent = { name: 'MockComponent' };
1757
+ registry.register('TestComponent', mockComponent);
1758
+ assert(registry.get('TestComponent') === mockComponent, 'Basic register/get', 'Component not retrieved correctly');
1759
+ // Test 3: Has method
1760
+ assert(registry.has('TestComponent') === true, 'Has method - existing', 'Should return true for existing component');
1761
+ assert(registry.has('NonExistent') === false, 'Has method - non-existing', 'Should return false for non-existing component');
1762
+ // Test 4: Register with metadata
1763
+ const mockSearchBox = { name: 'SearchBox' };
1764
+ registry.registerWithMetadata('SearchBox', 'CRM', 'v1', mockSearchBox, 'CRM-specific search');
1765
+ assert(registry.get('SearchBox_CRM_v1') === mockSearchBox, 'Register with metadata', 'Component not found with metadata key');
1766
+ assert(registry.get('SearchBox_CRM') === mockSearchBox, 'Backwards compatibility key', 'Component not found with context-only key');
1767
+ // Test 5: Multiple versions
1768
+ const mockSearchBoxV2 = { name: 'SearchBoxV2' };
1769
+ registry.registerWithMetadata('SearchBox', 'CRM', 'v2', mockSearchBoxV2);
1770
+ assert(registry.get('SearchBox_CRM_v1') === mockSearchBox, 'Version v1 still accessible', 'v1 component overwritten');
1771
+ assert(registry.get('SearchBox_CRM_v2') === mockSearchBoxV2, 'Version v2 accessible', 'v2 component not found');
1772
+ // Test 6: GetWithFallback - exact match
1773
+ const found1 = registry.getWithFallback('SearchBox', 'CRM', 'v2');
1774
+ assert(found1 === mockSearchBoxV2, 'GetWithFallback - exact match', 'Should find exact version match');
1775
+ // Test 7: GetWithFallback - context fallback
1776
+ const found2 = registry.getWithFallback('SearchBox', 'CRM', 'v3'); // v3 doesn't exist
1777
+ assert(found2 === mockSearchBoxV2, 'GetWithFallback - context fallback', 'Should fall back to context match');
1778
+ // Test 8: GetWithFallback - global fallback
1779
+ const globalComponent = { name: 'GlobalSearch' };
1780
+ registry.register('SearchBox_Global', globalComponent);
1781
+ const found3 = registry.getWithFallback('SearchBox', 'Finance', 'v1'); // Finance context doesn't exist
1782
+ assert(found3 === globalComponent, 'GetWithFallback - global fallback', 'Should fall back to global component');
1783
+ // Test 9: GetWithFallback - name only fallback
1784
+ const nameOnlyComponent = { name: 'NameOnly' };
1785
+ registry.register('UniqueComponent', nameOnlyComponent);
1786
+ const found4 = registry.getWithFallback('UniqueComponent', 'Any', 'v1');
1787
+ assert(found4 === nameOnlyComponent, 'GetWithFallback - name only fallback', 'Should fall back to name-only registration');
1788
+ // Test 10: GetWithFallback - not found
1789
+ const found5 = registry.getWithFallback('NotRegistered', 'Any', 'v1');
1790
+ assert(found5 === null, 'GetWithFallback - not found', 'Should return null when component not found');
1791
+ // Test 11: Get registered keys
1792
+ const keys = registry.getRegisteredKeys();
1793
+ assert(keys.includes('SearchBox_CRM_v1'), 'Get registered keys', 'Should include registered components');
1794
+ assert(keys.length > 5, 'Multiple registrations', `Should have multiple keys registered, found ${keys.length}`);
1795
+ // Test 12: Clear registry
1796
+ registry.clear();
1797
+ assert(registry.getRegisteredKeys().length === 0, 'Clear registry', 'Registry should be empty after clear');
1798
+ // Summary
1799
+ const passed = testResults.filter(r => r.passed).length;
1800
+ const failed = testResults.filter(r => !r.passed).length;
1801
+ console.log(`\nTest Summary: ${passed} passed, ${failed} failed out of ${testResults.length} total tests`);
1802
+ // Important: Clear the registry at the end of tests so it's ready for actual use
1803
+ registry.clear();
1804
+ return testResults;
1805
+ }
1806
+ /**
1807
+ * Compile and register a component from string code
1808
+ * This simulates how AI-generated components are processed
1809
+ */
1810
+ export async function compileAndRegisterComponent(componentName, componentCode, context = 'Global', version = 'v1') {
1811
+ const registry = GlobalComponentRegistry.Instance;
1812
+ try {
1813
+ // Get Babel for transpilation
1814
+ const Babel = window.Babel;
1815
+ if (!Babel) {
1816
+ console.error('Babel not loaded - cannot compile component');
1817
+ return false;
1818
+ }
1819
+ // Transpile the code
1820
+ const transpiledCode = Babel.transform(componentCode, {
1821
+ presets: ['react'],
1822
+ filename: `${componentName}.jsx`
1823
+ }).code;
1824
+ // Get React and other dependencies
1825
+ const React = window.React;
1826
+ const ReactDOM = window.ReactDOM;
1827
+ const libraries = {
1828
+ antd: window.antd,
1829
+ ReactBootstrap: window.ReactBootstrap,
1830
+ d3: window.d3,
1831
+ Chart: window.Chart,
1832
+ _: window._,
1833
+ dayjs: window.dayjs
1834
+ };
1835
+ // Create the component factory
1836
+ const createComponent = new Function('React', 'ReactDOM', 'useState', 'useEffect', 'useCallback', 'createStateUpdater', 'createStandardEventHandler', 'libraries', `${transpiledCode}; return createComponent;`)(React, ReactDOM, React.useState, React.useEffect, React.useCallback, () => { }, // createStateUpdater placeholder
1837
+ () => { }, // createStandardEventHandler placeholder
1838
+ libraries);
1839
+ // Get the component from the factory
1840
+ const componentResult = createComponent(React, ReactDOM, React.useState, React.useEffect, React.useCallback, () => { }, // createStateUpdater
1841
+ () => { } // createStandardEventHandler
1842
+ );
1843
+ // Register the component
1844
+ registry.registerWithMetadata(componentName, context, version, componentResult.component);
1845
+ console.log(`Compiled and registered component: ${componentName}`);
1846
+ return true;
1847
+ }
1848
+ catch (error) {
1849
+ console.error(`Failed to compile component ${componentName}:`, error);
1850
+ return false;
1851
+ }
1852
+ }
1853
+ /**
1854
+ * Helper function to register example components for testing
1855
+ * Call this during application initialization
1856
+ */
1857
+ export async function registerExampleComponents(React, Chart) {
1858
+ const registry = GlobalComponentRegistry.Instance;
1859
+ // Get React reference - either passed in or from window
1860
+ React = React || window.React;
1861
+ Chart = Chart || window.Chart;
1862
+ // Also make this function available globally for debugging
1863
+ window.registerExampleComponents = registerExampleComponents;
1864
+ window.compileAndRegisterComponent = compileAndRegisterComponent;
1865
+ if (React) {
1866
+ // Register simple test components (these use the real React components directly)
1867
+ registry.registerWithMetadata('SearchBox', 'CRM', 'v1', createSearchBoxComponent(React));
1868
+ registry.registerWithMetadata('SearchBox', 'Global', 'v1', createSearchBoxComponent(React));
1869
+ // Register OrderList variants
1870
+ registry.registerWithMetadata('OrderList', 'Standard', 'v1', createOrderListComponent(React));
1871
+ registry.registerWithMetadata('OrderList', 'Advanced', 'v1', createOrderListComponent(React));
1872
+ // Register chart components
1873
+ if (Chart) {
1874
+ registry.registerWithMetadata('CategoryChart', 'Global', 'v1', createCategoryChartComponent(React, Chart));
1875
+ }
1876
+ // Compile and register Action browser components from strings
1877
+ // This simulates how AI-generated components are processed
1878
+ await compileAndRegisterComponent('ActionCategoryList', getActionCategoryListComponentString(), 'Global', 'v1');
1879
+ await compileAndRegisterComponent('ActionList', getActionListComponentString(), 'Global', 'v1');
1880
+ await compileAndRegisterComponent('ActionBrowser', getActionBrowserComponentString(), 'Global', 'v1');
1881
+ console.log('Example components registered successfully');
1882
+ console.log('Registered components:', registry.getRegisteredKeys());
1883
+ return true;
1884
+ }
1885
+ else {
1886
+ console.warn('React not found - cannot register example components');
1887
+ return false;
1888
+ }
1889
+ }
668
1890
  //# sourceMappingURL=skip-react-component-host.js.map