neo-cmp-cli 1.13.16 → 1.13.18

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.
Files changed (182) hide show
  1. package/README.md +2 -1
  2. package/dist/index2.js +1 -1
  3. package/dist/main2.js +1 -1
  4. package/dist/neo/env.js +1 -1
  5. package/dist/neo/neoLogin.js +1 -1
  6. package/dist/neo/pushCmp.js +1 -1
  7. package/dist/package.json.js +1 -1
  8. package/package.json +3 -2
  9. package/template/antd-custom-cmp-template/package.json +1 -1
  10. package/template/asset-manage-template/docs/README.md +1 -232
  11. package/template/asset-manage-template/package.json +2 -2
  12. package/template/echarts-custom-cmp-template/package.json +1 -1
  13. package/template/empty-custom-cmp-template/package.json +2 -2
  14. package/template/map-custom-cmp-template/package.json +1 -1
  15. package/template/neo-bi-cmps/neo.config.js +7 -1
  16. package/template/neo-bi-cmps/package.json +8 -7
  17. package/template/neo-bi-cmps/public/403.html +77 -0
  18. package/template/neo-bi-cmps/public/demo.html +2453 -0
  19. package/template/neo-bi-cmps/src/assets/icon/barChart.svg +1 -0
  20. package/template/neo-bi-cmps/src/assets/icon/card.svg +1 -0
  21. package/template/neo-bi-cmps/src/assets/icon/filter.svg +1 -0
  22. package/template/neo-bi-cmps/src/assets/icon/funnel.svg +1 -0
  23. package/template/neo-bi-cmps/src/assets/icon/tab.svg +1 -0
  24. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +3 -14
  25. package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +29 -0
  26. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +668 -146
  27. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +26 -48
  28. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +46 -139
  29. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +11 -10
  30. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +9 -16
  31. package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
  32. package/template/neo-bi-cmps/src/utils/common.ts +231 -0
  33. package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +268 -0
  34. package/template/neo-bi-cmps/src/utils/filterBar.ts +140 -0
  35. package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +341 -0
  36. package/template/{neo-h5-cmps/src/utils/queryObjectData.ts → neo-bi-cmps/src/utils/queryByCustomSQL.ts} +18 -13
  37. package/template/neo-bi-cmps/src/utils/requestDebounce.ts +22 -0
  38. package/template/neo-bi-cmps/src/utils/simpleTable.tsx +344 -0
  39. package/template/neo-bi-cmps/src/utils/stageSwitch.ts +15 -0
  40. package/template/neo-bi-cmps/src/utils/stageTimeChart.ts +90 -0
  41. package/template/neo-bi-cmps/src/utils/targetNumber.ts +12 -0
  42. package/template/neo-custom-cmp-template/docs/README.md +0 -231
  43. package/template/neo-custom-cmp-template/package.json +2 -2
  44. package/template/neo-h5-cmps/package.json +2 -2
  45. package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +1 -2
  46. package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +1 -1
  47. package/template/neo-h5-cmps/src/components/globalSearchInput__c/index.tsx +1 -1
  48. package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +1 -2
  49. package/template/neo-order-cmps/package.json +2 -2
  50. package/template/neo-pipeline-cmps/.prettierrc.js +12 -0
  51. package/template/neo-pipeline-cmps/@types/neo-ui-common.d.ts +36 -0
  52. package/template/neo-pipeline-cmps/README.md +99 -0
  53. package/template/neo-pipeline-cmps/commitlint.config.js +59 -0
  54. package/template/neo-pipeline-cmps/neo.config.js +135 -0
  55. package/template/neo-pipeline-cmps/package.json +66 -0
  56. package/template/neo-pipeline-cmps/public/403.html +77 -0
  57. package/template/neo-pipeline-cmps/public/css/base.css +283 -0
  58. package/template/neo-pipeline-cmps/public/demo.html +2453 -0
  59. package/template/neo-pipeline-cmps/public/scripts/app/bluebird.js +6679 -0
  60. package/template/neo-pipeline-cmps/public/template.html +13 -0
  61. package/template/neo-pipeline-cmps/src/assets/css/common.scss +127 -0
  62. package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +47 -0
  63. package/template/neo-pipeline-cmps/src/assets/icon/barChart.svg +1 -0
  64. package/template/neo-pipeline-cmps/src/assets/icon/card.svg +1 -0
  65. package/template/neo-pipeline-cmps/src/assets/icon/filter.svg +1 -0
  66. package/template/neo-pipeline-cmps/src/assets/icon/funnel.svg +1 -0
  67. package/template/neo-pipeline-cmps/src/assets/icon/tab.svg +1 -0
  68. package/template/neo-pipeline-cmps/src/assets/img/AIBtn.gif +0 -0
  69. package/template/neo-pipeline-cmps/src/assets/img/NeoCRM.jpg +0 -0
  70. package/template/neo-pipeline-cmps/src/assets/img/aiLogo.png +0 -0
  71. package/template/neo-pipeline-cmps/src/assets/img/card-list.svg +1 -0
  72. package/template/neo-pipeline-cmps/src/assets/img/contact-form.svg +1 -0
  73. package/template/neo-pipeline-cmps/src/assets/img/custom-form.svg +1 -0
  74. package/template/neo-pipeline-cmps/src/assets/img/custom-widget.svg +1 -0
  75. package/template/neo-pipeline-cmps/src/assets/img/data-list.svg +1 -0
  76. package/template/neo-pipeline-cmps/src/assets/img/detail.svg +1 -0
  77. package/template/neo-pipeline-cmps/src/assets/img/favicon.png +0 -0
  78. package/template/neo-pipeline-cmps/src/assets/img/map.svg +1 -0
  79. package/template/neo-pipeline-cmps/src/assets/img/search.svg +1 -0
  80. package/template/neo-pipeline-cmps/src/assets/img/table.svg +1 -0
  81. package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +24 -0
  82. package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +29 -0
  83. package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +731 -0
  84. package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +52 -0
  85. package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
  86. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +39 -0
  87. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +416 -0
  88. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +80 -0
  89. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/style.scss +83 -0
  90. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +470 -0
  91. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +45 -0
  92. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/style.scss +137 -0
  93. package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +89 -0
  94. package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +195 -0
  95. package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +667 -0
  96. package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +124 -0
  97. package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +192 -0
  98. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +36 -0
  99. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +513 -0
  100. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +71 -0
  101. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/style.scss +4 -2
  102. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +37 -0
  103. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +455 -0
  104. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +106 -0
  105. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/style.scss +3 -2
  106. package/template/neo-pipeline-cmps/src/utils/common.ts +229 -0
  107. package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +266 -0
  108. package/template/neo-pipeline-cmps/src/utils/filterBar.ts +140 -0
  109. package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +343 -0
  110. package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +121 -0
  111. package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
  112. package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +349 -0
  113. package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +15 -0
  114. package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +90 -0
  115. package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +12 -0
  116. package/template/neo-pipeline-cmps/tsconfig.json +40 -0
  117. package/template/neo-web-entity-grid/package.json +2 -2
  118. package/template/neo-web-form/package.json +2 -2
  119. package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +161 -41
  120. package/template/neo-web-form/src/components/batchAddTable__c/model.ts +4 -2
  121. package/template/react-custom-cmp-template/package.json +1 -1
  122. package/template/react-ts-custom-cmp-template/package.json +1 -1
  123. package/template/vue2-custom-cmp-template/package.json +1 -1
  124. package/template/asset-manage-template/src/utils/axiosFetcher.ts +0 -37
  125. package/template/asset-manage-template/src/utils/queryObjectData.ts +0 -112
  126. package/template/asset-manage-template/src/utils/xobjects.ts +0 -162
  127. package/template/neo-bi-cmps/.npmrc copy +0 -1
  128. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +0 -52
  129. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +0 -183
  130. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +0 -90
  131. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +0 -218
  132. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +0 -31
  133. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +0 -158
  134. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +0 -40
  135. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +0 -154
  136. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +0 -36
  137. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +0 -86
  138. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +0 -62
  139. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +0 -48
  140. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +0 -24
  141. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +0 -100
  142. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +0 -46
  143. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +0 -60
  144. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +0 -35
  145. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +0 -70
  146. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +0 -50
  147. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +0 -33
  148. package/template/neo-bi-cmps/src/components/oppList__c/README.md +0 -52
  149. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +0 -285
  150. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +0 -86
  151. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +0 -133
  152. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +0 -39
  153. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +0 -130
  154. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +0 -66
  155. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +0 -133
  156. package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +0 -36
  157. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +0 -118
  158. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +0 -92
  159. package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +0 -37
  160. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +0 -126
  161. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +0 -57
  162. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +0 -37
  163. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +0 -80
  164. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +0 -45
  165. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +0 -37
  166. package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +0 -37
  167. package/template/neo-bi-cmps/src/utils/queryObjectData.ts +0 -76
  168. package/template/neo-bi-cmps/src/utils/xobjects.ts +0 -162
  169. package/template/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -37
  170. package/template/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -112
  171. package/template/neo-custom-cmp-template/src/utils/xobjects.ts +0 -162
  172. package/template/neo-h5-cmps/src/utils/axiosFetcher.ts +0 -37
  173. package/template/neo-h5-cmps/src/utils/xobjects.ts +0 -167
  174. package/template/neo-order-cmps/src/utils/axiosFetcher.ts +0 -37
  175. package/template/neo-order-cmps/src/utils/queryObjectData.ts +0 -112
  176. package/template/neo-order-cmps/src/utils/xobjects.ts +0 -162
  177. package/template/neo-web-entity-grid/src/utils/axiosFetcher.ts +0 -37
  178. package/template/neo-web-entity-grid/src/utils/queryObjectData.ts +0 -112
  179. package/template/neo-web-entity-grid/src/utils/xobjects.ts +0 -167
  180. package/template/neo-web-form/src/utils/axiosFetcher.ts +0 -37
  181. package/template/neo-web-form/src/utils/queryObjectData.ts +0 -112
  182. package/template/neo-web-form/src/utils/xobjects.ts +0 -167
@@ -0,0 +1,667 @@
1
+ /**
2
+ * @file XObject data table component (simplified version)
3
+ * @description XObject entity data table component based on the Neo platform, supports data display only
4
+ */
5
+ import * as React from 'react';
6
+ import { Table, Spin, Empty, Popover } from 'antd';
7
+ // @ts-ignore
8
+ import { xObject } from 'neo-open-api'; // Neo Open API
9
+ // @ts-ignore
10
+ import isEqual from 'lodash/isEqual';
11
+
12
+ // Neo Open API// Import neo-ui-common / BaseCmp
13
+ // @ts-ignore
14
+ import { BaseCmp } from 'neo-ui-common';
15
+
16
+ // Import neo-ui-common / NeoEvent
17
+ // @ts-ignore
18
+ import { NeoEvent } from 'neo-ui-common';
19
+
20
+ import queryByCustomSQL from '../../utils/queryByCustomSQL';
21
+ import {
22
+ closeRangeFromFilter,
23
+ entityTypeIdForWhere,
24
+ normalizeOwnerIdsForWhere,
25
+ getDefaultFilterByProps,
26
+ } from '../../utils/common';
27
+ import {
28
+ buildHistoryMap,
29
+ closeDateChangeTooltipTitle,
30
+ closeDateTrend,
31
+ formatActivityDateTime,
32
+ formatCloseDate,
33
+ formatMoneyCell,
34
+ formatMoneySummaryAmount,
35
+ formatPercentCell,
36
+ isMoneyField,
37
+ moneyChangeTooltipTitle,
38
+ moneyTrend,
39
+ renderOppHealthAssessmentLevel,
40
+ renderTrendCell,
41
+ renderWinRateHoverCard,
42
+ sumMoneyRows,
43
+ type FieldInfo,
44
+ type HistoryOppSnap,
45
+ } from '../../utils/simpleTable';
46
+
47
+ import ShowHealthResult from '../showHealthResult__c';
48
+
49
+ import './common.scss';
50
+ import './style.scss';
51
+
52
+ export type { HistoryOppSnap } from '../../utils/simpleTable';
53
+
54
+ /** Opportunity detail page hash route (consistent with platform neoweb) */
55
+ function buildOpportunityDetailUrl(id: string, objectApiKey: string): string {
56
+ const q = new URLSearchParams({
57
+ objectApiKey,
58
+ recordId: id,
59
+ });
60
+ return `/bff/neoweb#/entityDetail/opportunity/${id}?${q.toString()}`;
61
+ }
62
+
63
+ /** Fields not displayed in the table (can still be fetched via xObjectDataApi.fields, used for hover cards / tooltips) */
64
+ const HIDDEN_TABLE_COLUMN_KEYS = new Set([
65
+ 'customItem244__c',
66
+ 'customItem241__c',
67
+ 'customItem242__c',
68
+ 'customItem246__c',
69
+ 'customItem247__c',
70
+ 'customItem245__c',
71
+ // 'objectId', // Needed for navigating to detail page
72
+ // 'businessTypeId' // Needed for navigating to detail page
73
+ ]);
74
+
75
+ /** Historical opportunity SQL consistent with stageSwitch; field order must match query fields (array rows parsed by column index) */
76
+ const HISTORY_OPPORTUNITY_BI_KEY = 'biCustomModel_397169_20260401104916618';
77
+ const HISTORY_QUERY_FIELDS = [
78
+ // 'id', // Historical version ID
79
+ 'opportunity_1__id', // Opportunity ID
80
+ 'opportunity_1_closeDate',
81
+ 'opportunity_1_opportunityName',
82
+ 'opportunity_1_saleStageId',
83
+ 'opportunity_1_money',
84
+ ] as const;
85
+
86
+ const defaultXObjectDataApi = {
87
+ xObjectApiKey: 'opportunity',
88
+ fields: [
89
+ 'opportunityName', // OpportunityStageName Opportunity Name
90
+ 'money', // Amount
91
+ 'closeDate', // Close Date
92
+ 'customItem248__c', // Stage
93
+ // 'oppHealthAssessmentScore',
94
+ 'recentActivityRecordTime',
95
+ 'oppHealthAssessmentLevel', // 1.AI Score; hover card: calls opportunity health assessment API
96
+ 'customItem239__c', // 2.AI Win Rate: 3 fields shown in hover card (baseline, positiveFactors, negativeFactors)
97
+ 'customItem244__c', // baseline
98
+ 'customItem241__c', // positiveFactors
99
+ 'customItem242__c', // negativeFactors
100
+ 'customItem246__c', // AmoutChanged
101
+ 'customItem247__c', // ClosedDateChanged
102
+ 'customItem245__c', // StageChanged
103
+ // 'objectId', // Needed for navigating to detail page
104
+ // 'businessTypeId' // Needed for navigating to detail page
105
+ ],
106
+ page: 1,
107
+ pageSize: 50,
108
+ };
109
+ /**
110
+ * Component props interface
111
+ */
112
+ interface SimpleTableProps {
113
+ /** XObject entity API Key, used to identify the data object to operate on */
114
+ xObjectDataApi: {
115
+ xObjectApiKey: string;
116
+ fields: string[];
117
+ fieldDescList?: any[];
118
+ autoFetchData?: boolean;
119
+ };
120
+ /** Neo platform data, including system information */
121
+ data?: any;
122
+ entityData?: any; // Entity data automatically fetched by Neo platform
123
+ /** Whether it is a custom entity object */
124
+ custom?: boolean;
125
+ /** Component class name */
126
+ className?: string;
127
+ /** Table title, takes priority */
128
+ title?: string;
129
+ /** Description text below the title */
130
+ description?: string;
131
+ /** Currently active sales stage */
132
+ activeStage?: string;
133
+ }
134
+
135
+ /**
136
+ * Component state interface
137
+ */
138
+ interface SimpleTableState {
139
+ /** Table title */
140
+ title?: string;
141
+ fieldList: FieldInfo[];
142
+ /** Table data source */
143
+ dataSource: any[];
144
+ /** Loading state */
145
+ loading: boolean;
146
+ /** Error message */
147
+ error: string | null;
148
+ activeStage: string; // Currently active sales stage
149
+ /**
150
+ * Consistent with FilterBar event payload (closeDateCustomRange / opportunityOwner / businessType, etc.);
151
+ * Default value is defaultFilter, getFilterWhere only reads from it
152
+ */
153
+ filter: any;
154
+ }
155
+
156
+ /**
157
+ * XObject data table component (simplified version)
158
+ * Supports data display only, does not support CRUD operations
159
+ */
160
+ export default class SimpleTable extends BaseCmp<
161
+ SimpleTableProps,
162
+ SimpleTableState
163
+ > {
164
+ constructor(props: SimpleTableProps) {
165
+ super(props);
166
+
167
+ // Initialize component state
168
+ this.state = {
169
+ fieldList: [],
170
+ dataSource: [],
171
+ loading: false,
172
+ error: null,
173
+ activeStage: props.activeStage || 'Prospecting',
174
+ filter: getDefaultFilterByProps(props),
175
+ };
176
+
177
+ this.loadData = this.loadData.bind(this);
178
+ this.loadFieldList = this.loadFieldList.bind(this);
179
+ this.getCurFieldList = this.getCurFieldList.bind(this);
180
+ this.getMoneyFieldApiKey = this.getMoneyFieldApiKey.bind(this);
181
+ this.generateColumns = this.generateColumns.bind(this);
182
+ this.getFilterWhere = this.getFilterWhere.bind(this);
183
+ this.setFilter = this.setFilter.bind(this);
184
+ this.updateActiveStage = this.updateActiveStage.bind(this);
185
+ }
186
+
187
+ componentDidMount() {
188
+ // const { xObjectDataApi } = this.props;
189
+ // const { xObjectApiKey } = xObjectDataApi || {};
190
+ const { xObjectApiKey } = defaultXObjectDataApi;
191
+
192
+ if (xObjectApiKey) {
193
+ // Initialize field list and load data
194
+ this.loadFieldList();
195
+ this.loadData();
196
+ }
197
+
198
+ /*
199
+ // Broadcast event: update FilterBar filter
200
+ NeoEvent.listen('updateFilterData', (filterData: any) => {
201
+ console.log('SimpleTable received a broadcast event updateFilterData: ', filterData);
202
+ this.setFilter(filterData);
203
+ });
204
+
205
+ // Broadcast event: update the currently active sales stage
206
+ NeoEvent.listen('updateActiveStage', (activeStage: string) => {
207
+ console.log('SimpleTable received a broadcast event updateActiveStage: ', activeStage);
208
+ this.updateActiveStage(activeStage);
209
+ });
210
+ */
211
+ }
212
+
213
+ /**
214
+ * Load field list
215
+ * Fetch XObject field description information from the Neo platform
216
+ */
217
+ async loadFieldList() {
218
+ const xObjectDataApi: any = defaultXObjectDataApi;
219
+
220
+ // Method 1: Get field descriptions directly from props.xObjectDetailApi
221
+ if (xObjectDataApi && xObjectDataApi.fieldDescList) {
222
+ this.setState({ fieldList: xObjectDataApi.fieldDescList });
223
+ } else {
224
+ // Method 2: Fetch field descriptions via OpenAPI SDK
225
+ if (!xObjectDataApi.xObjectApiKey) return;
226
+ try {
227
+ const resultData = await xObject.getDesc(xObjectDataApi.xObjectApiKey);
228
+ if (resultData && resultData.status) {
229
+ const result = resultData.data || {};
230
+ const fieldList = result.fields || [];
231
+ this.setState({ fieldList, title: result.label });
232
+ }
233
+ } catch (error) {
234
+ console.error('Failed to get field list:', error);
235
+ }
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Current visible field list for the table (consistent with column configuration)
241
+ */
242
+ getCurFieldList(): FieldInfo[] {
243
+ // const { xObjectDataApi } = this.props;
244
+ const xObjectDataApi: any = defaultXObjectDataApi;
245
+ const { fieldList } = this.state;
246
+ const { fields, fieldDescList } = xObjectDataApi || {};
247
+ let curFieldList =
248
+ fieldDescList && fieldDescList.length > 0 ? fieldDescList : fieldList;
249
+
250
+ if (fields && fields.length > 0) {
251
+ curFieldList = curFieldList.filter((field: FieldInfo) =>
252
+ fields.includes(field.apiKey),
253
+ );
254
+ }
255
+ return curFieldList;
256
+ }
257
+
258
+ /**
259
+ * Money field apiKey for summary (consistent with the money column, takes the first currency field)
260
+ */
261
+ getMoneyFieldApiKey(): string | null {
262
+ const moneyField = this.getCurFieldList().find((f) => isMoneyField(f));
263
+ return moneyField?.apiKey ?? null;
264
+ }
265
+
266
+ /**
267
+ * Generate table column configuration
268
+ * @returns Array of table column configurations
269
+ */
270
+ generateColumns() {
271
+ const xObjectDataApi: any = defaultXObjectDataApi;
272
+
273
+ const curFieldList = this.getCurFieldList().filter(
274
+ (field) => !HIDDEN_TABLE_COLUMN_KEYS.has(field.apiKey),
275
+ );
276
+
277
+ // Generate base column configuration from field list
278
+ const columns: any[] = curFieldList.map((field) => {
279
+ const col: any = {
280
+ title: field.label,
281
+ dataIndex: field.apiKey,
282
+ key: field.apiKey,
283
+ ellipsis: true,
284
+ };
285
+ if (
286
+ field.apiKey === 'oppHealthAssessmentLevel' ||
287
+ field.apiKey === 'customItem239__c'
288
+ ) {
289
+ col.title = (
290
+ <span className="simpleTable-th-title-with-ai-icon">
291
+ <span className="simpleTable-th-ai-icon" />
292
+ <span>{field.label}</span>
293
+ </span>
294
+ );
295
+ }
296
+ if (field.apiKey === 'closeDate') {
297
+ col.render = (text: unknown, record: any) => {
298
+ const display = formatCloseDate(text);
299
+ const trend = closeDateTrend(text, record?.historyData);
300
+ if (trend !== 'up' && trend !== 'down') {
301
+ return display;
302
+ }
303
+ return renderTrendCell({
304
+ displayText: display,
305
+ trend,
306
+ tooltip: closeDateChangeTooltipTitle(record, text),
307
+ });
308
+ };
309
+ } else if (field.apiKey === 'opportunityName') {
310
+ col.render = (text: unknown, record: any) => {
311
+ const display = text == null || text === '' ? '—' : String(text);
312
+ const id =
313
+ record?.id != null && record.id !== '' ? String(record.id) : '';
314
+ const objectApiKey = xObjectDataApi?.xObjectApiKey;
315
+ if (!id || !objectApiKey) {
316
+ return display;
317
+ }
318
+ const url = buildOpportunityDetailUrl(id, objectApiKey);
319
+ return (
320
+ <a
321
+ className="simpleTable-opp-name-link"
322
+ href={url}
323
+ target="_blank"
324
+ /*
325
+ onClick={(e) => {
326
+ e.preventDefault();
327
+ window.location.assign(url);
328
+ }}
329
+ */
330
+ >
331
+ {display}
332
+ </a>
333
+ );
334
+ };
335
+ } else if (field.apiKey === 'recentActivityRecordTime') {
336
+ col.render = (text: unknown) => formatActivityDateTime(text);
337
+ } else if (field.apiKey === 'oppHealthAssessmentLevel') {
338
+ col.render = (text: unknown, record: any) => {
339
+ const cell = renderOppHealthAssessmentLevel(text);
340
+ const id =
341
+ record?.id != null && record.id !== '' ? String(record.id) : '';
342
+ const xObjectApiKey = xObjectDataApi?.xObjectApiKey;
343
+ if (!id || !xObjectApiKey) {
344
+ return cell;
345
+ }
346
+ return (
347
+ <Popover
348
+ content={
349
+ <div className="simpleTable-health-popover-body">
350
+ <ShowHealthResult id={id} xObjectApiKey={xObjectApiKey} />
351
+ </div>
352
+ }
353
+ trigger="hover"
354
+ placement="topLeft"
355
+ overlayClassName="simpleTable-health-popover-overlay"
356
+ mouseEnterDelay={0.08}
357
+ >
358
+ <span className="simpleTable-health-popover-trigger">{cell}</span>
359
+ </Popover>
360
+ );
361
+ };
362
+ } else if (field.apiKey === 'customItem239__c') {
363
+ col.align = 'center';
364
+ col.render = (_text: unknown, record: any) => (
365
+ <Popover
366
+ content={renderWinRateHoverCard(record)}
367
+ trigger="hover"
368
+ placement="topLeft"
369
+ overlayClassName="simpleTable-winrate-popover"
370
+ mouseEnterDelay={0.08}
371
+ >
372
+ <span className="simpleTable-winrate-trigger">
373
+ {formatPercentCell(record?.customItem239__c)}
374
+ </span>
375
+ </Popover>
376
+ );
377
+ } else if (isMoneyField(field)) {
378
+ // col.align = 'right';
379
+ col.render = (text: unknown, record: any) => {
380
+ const display = formatMoneyCell(text);
381
+ const trend = moneyTrend(text, record?.historyData);
382
+ if (trend !== 'up' && trend !== 'down') {
383
+ return display;
384
+ }
385
+ return renderTrendCell({
386
+ displayText: display,
387
+ trend,
388
+ tooltip: moneyChangeTooltipTitle(record, text),
389
+ });
390
+ };
391
+ }
392
+ return col;
393
+ });
394
+
395
+ return columns;
396
+ }
397
+
398
+ /**
399
+ * Convert FilterBar structured filter to xObject.query where string array (consistent with stageSwitch, SDK concatenates with AND in order)
400
+ */
401
+ getFilterWhere(): string[] {
402
+ const raw = this.state.filter;
403
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
404
+ return [];
405
+ }
406
+ const curFilter = raw as Record<string, unknown>;
407
+ const where: string[] = [];
408
+
409
+ const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
410
+ if (range) {
411
+ where.push(`closeDate >= ${range.start} and closeDate <= ${range.end}`);
412
+ }
413
+
414
+ const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
415
+ if (ownerIds.length === 1) {
416
+ where.push(`ownerId = ${ownerIds[0]}`);
417
+ } else if (ownerIds.length > 1) {
418
+ where.push(`ownerId in (${ownerIds.join(', ')})`);
419
+ }
420
+
421
+ const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
422
+ if (entityTypeId != null) {
423
+ where.push(`entityType = ${entityTypeId}`);
424
+ }
425
+
426
+ /*
427
+ // If activeStage exists, add stage filter condition
428
+ if (this.state.activeStage) {
429
+ // customItem248__c is the sales stage field
430
+ where.push(`customItem248__c = '${this.state.activeStage}'`);
431
+ }
432
+ */
433
+
434
+ return where;
435
+ }
436
+
437
+ /** Consistent with stageSwitch: historical opportunity custom SQL where (opportunity_1_* fields) */
438
+ getHistoryOpportunityQueryFilter(): string[] {
439
+ const raw = this.state.filter;
440
+ const defaultFilter = getDefaultFilterByProps(this.props);
441
+
442
+ const curFilter: Record<string, unknown> =
443
+ raw && typeof raw === 'object' && !Array.isArray(raw)
444
+ ? Object.keys(raw as object).length > 0
445
+ ? (raw as Record<string, unknown>)
446
+ : defaultFilter
447
+ : defaultFilter;
448
+
449
+ const where: string[] = [];
450
+
451
+ const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
452
+ if (range) {
453
+ where.push(
454
+ `opportunity_1_closeDate >= ${range.start} and opportunity_1_closeDate <= ${range.end}`,
455
+ );
456
+ }
457
+
458
+ const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
459
+ if (ownerIds.length === 1) {
460
+ where.push(`opportunity_1_ownerId = ${ownerIds[0]}`);
461
+ } else if (ownerIds.length > 1) {
462
+ where.push(`opportunity_1_ownerId in (${ownerIds.join(', ')})`);
463
+ }
464
+
465
+ const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
466
+ if (entityTypeId != null) {
467
+ where.push(`opportunity_1_entityType = ${entityTypeId}`);
468
+ }
469
+
470
+ if (curFilter.changesSinceCustomTime) {
471
+ where.push(`version = ${curFilter.changesSinceCustomTime}`);
472
+ }
473
+
474
+ return where;
475
+ }
476
+
477
+ /** Query historical opportunity list for the period (same source as stageSwitch.fetchHistoryOpportunityList, fields extended as needed) */
478
+ async fetchHistoryOpportunityList(): Promise<unknown[]> {
479
+ const result = await queryByCustomSQL({
480
+ xObjectApiKey: HISTORY_OPPORTUNITY_BI_KEY,
481
+ fields: [...HISTORY_QUERY_FIELDS],
482
+ page: 1,
483
+ pageSize: 1000,
484
+ where: this.getHistoryOpportunityQueryFilter(),
485
+ });
486
+
487
+ return result?.status ? result.data ?? [] : [];
488
+ }
489
+
490
+ /**
491
+ * Load table data
492
+ */
493
+ @NeoEvent.function
494
+ async loadData() {
495
+ // const api = this.props.xObjectDataApi || {};
496
+ const api: any = defaultXObjectDataApi;
497
+ const { xObjectApiKey } = api;
498
+ if (!xObjectApiKey) return;
499
+
500
+ this.setState({ loading: true, error: null });
501
+
502
+ try {
503
+ const rawWhere = (api as { where?: unknown }).where;
504
+ const baseWhere: string[] = Array.isArray(rawWhere)
505
+ ? rawWhere.filter((w): w is string => typeof w === 'string')
506
+ : typeof rawWhere === 'string' && rawWhere
507
+ ? [rawWhere]
508
+ : [];
509
+ const filterWhere =
510
+ xObjectApiKey === 'opportunity' ? this.getFilterWhere() : [];
511
+ const [result, historyRows] = await Promise.all([
512
+ xObject.query({
513
+ ...api,
514
+ where: [...baseWhere, ...filterWhere],
515
+ }),
516
+ this.fetchHistoryOpportunityList(),
517
+ ]);
518
+
519
+ if (result && result.status) {
520
+ let records = result.data || [];
521
+
522
+ console.log('[SimpleTable__c] records:', records, historyRows);
523
+
524
+ // Filter data by stage
525
+ if (this.state.activeStage) {
526
+ records = records.filter((record: any) => {
527
+ return record.customItem248__c === this.state.activeStage;
528
+ });
529
+ }
530
+
531
+ const historyMap =
532
+ xObjectApiKey === 'opportunity'
533
+ ? buildHistoryMap(historyRows)
534
+ : new Map<string, HistoryOppSnap>();
535
+
536
+ const merged = records.map((r: any) => {
537
+ const id = r?.id != null ? String(r.id) : '';
538
+ const historyData = id ? historyMap.get(id) ?? null : null;
539
+ return { ...r, historyData };
540
+ });
541
+
542
+ this.setState({
543
+ dataSource: merged,
544
+ loading: false,
545
+ });
546
+ } else {
547
+ this.setState({
548
+ error: result?.msg || 'Failed to fetch data',
549
+ loading: false,
550
+ });
551
+ }
552
+ } catch (error: any) {
553
+ this.setState({
554
+ error: error.message || 'Failed to fetch data',
555
+ loading: false,
556
+ });
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Consistent with stageSwitch: update FilterBar filter and re-fetch table data.
562
+ */
563
+ @NeoEvent.function
564
+ setFilter(filter?: any) {
565
+ if (isEqual(filter, this.state.filter)) {
566
+ return;
567
+ }
568
+ console.log('[SimpleTable__c] setFilter:', filter);
569
+ this.setState({ filter }, () => {
570
+ this.loadData();
571
+ });
572
+ }
573
+
574
+ /**
575
+ * Update the currently active sales stage
576
+ */
577
+ @NeoEvent.function
578
+ updateActiveStage(activeStage?: string) {
579
+ if (isEqual(activeStage, this.state.activeStage)) {
580
+ return;
581
+ }
582
+ console.log('[SimpleTable__c] updateActiveStage:', activeStage);
583
+ this.setState({ activeStage: activeStage || '' }, () => {
584
+ this.loadData();
585
+ });
586
+ }
587
+
588
+ /**
589
+ * Render component
590
+ * @returns Component JSX element
591
+ */
592
+ render() {
593
+ const { title: stateTitle, dataSource, loading, error } = this.state;
594
+ const { className, title: propsTitle, description } = this.props;
595
+ // const { xObjectApiKey } = this.props.xObjectDataApi || {};
596
+ const { xObjectApiKey } = defaultXObjectDataApi;
597
+ const columns = this.generateColumns();
598
+
599
+ // Use props.title first, then state.title, and finally the default value
600
+ const displayTitle = propsTitle || stateTitle || 'Data Table';
601
+
602
+ const moneyKey = this.getMoneyFieldApiKey();
603
+ const totalAmount = moneyKey ? sumMoneyRows(dataSource, moneyKey) : 0;
604
+ const oppCount = dataSource.length;
605
+ const oppWord = oppCount === 1 ? 'opportunity' : 'opportunities';
606
+ const summaryText = `Total: ${formatMoneySummaryAmount(
607
+ totalAmount,
608
+ )} · ${oppCount} ${oppWord}`;
609
+
610
+ console.log(
611
+ 'simpleTable__c / dataSource:',
612
+ dataSource,
613
+ this.props,
614
+ this.state,
615
+ );
616
+
617
+ return (
618
+ <div className={`simpleTable__c ${className}`} data-time="2026.4.17 01">
619
+ <div className="table-wrapper">
620
+ <div className="panel-header">
621
+ <div className="panel-header-text">
622
+ <h3>
623
+ {displayTitle} - {this.state.activeStage}
624
+ </h3>
625
+ {description ? (
626
+ <p className="panel-header-description">{description}</p>
627
+ ) : null}
628
+ </div>
629
+ <div className="panel-header-summary" title={summaryText}>
630
+ Total:{' '}
631
+ <span className="summary-amount">
632
+ {formatMoneySummaryAmount(totalAmount)}
633
+ </span>
634
+ {' · '}
635
+ <span className="summary-amount">{oppCount}</span> {oppWord}
636
+ </div>
637
+ </div>
638
+ <div className="table-container">
639
+ <Spin spinning={loading} tip="Loading data...">
640
+ {error ? (
641
+ <Empty
642
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
643
+ description={
644
+ <div>
645
+ <div style={{ color: '#ff4d4f', marginBottom: 8 }}>
646
+ {error}
647
+ </div>
648
+ </div>
649
+ }
650
+ />
651
+ ) : (
652
+ <Table
653
+ key={`${xObjectApiKey}-${columns.length}`}
654
+ columns={columns}
655
+ dataSource={dataSource}
656
+ rowKey="id"
657
+ pagination={false}
658
+ scroll={{ x: 'max-content' }}
659
+ />
660
+ )}
661
+ </Spin>
662
+ </div>
663
+ </div>
664
+ </div>
665
+ );
666
+ }
667
+ }