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,731 @@
1
+ /**
2
+ * @file Filter Bar Component
3
+ * @description Supports multi-dimensional filtering by date range, owner (user entity), business type, etc.
4
+ */
5
+ import * as React from 'react';
6
+ import { DatePicker, Select, Spin } from 'antd';
7
+ import moment, { Moment } from 'moment';
8
+ // @ts-ignore
9
+ import { request, xObject } from 'neo-open-api';
10
+ // @ts-ignore
11
+ import { BaseCmp, NeoEvent } from 'neo-ui-common';
12
+
13
+ import { getDefaultOpportunityOwnerIds } from '../../utils/common';
14
+
15
+ import {
16
+ defaultChangesSinceOptions,
17
+ momentRangeToTimestamps,
18
+ normalizeOptions,
19
+ parseBusinessTypes,
20
+ parseUserRecordsToOwnerOptions,
21
+ relativeCloseDateRangeFromCode,
22
+ resolveDefaultBusinessTypeValue,
23
+ type FilterOption,
24
+ type TimestampRange,
25
+ } from '../../utils/filterBar';
26
+
27
+ export type { FilterOption, TimestampRange } from '../../utils/filterBar';
28
+
29
+ import './common.scss';
30
+ import './style.scss';
31
+
32
+ const { RangePicker } = DatePicker;
33
+
34
+ const BUSI_TYPE_URL = '/rest/data/v2.0/xobjects/opportunity/busiType';
35
+
36
+ /** Opportunity Owner personnel list: platform user entity */
37
+ const USER_ENTITY_API_KEY = 'user';
38
+ const USER_QUERY_FIELDS = [
39
+ 'id',
40
+ 'name',
41
+ 'nickName',
42
+ 'personalEmail',
43
+ 'telephone',
44
+ ] as const;
45
+
46
+ interface FilterBarEventData {
47
+ data: FilterBarChangePayload;
48
+ }
49
+
50
+ export interface FilterBarChangePayload {
51
+ closeDate: number | string;
52
+ closeDateCustomRange?: TimestampRange;
53
+ /** Opportunity Owner multi-select: personnel id list, empty array means none selected */
54
+ opportunityOwner: (number | string)[];
55
+ businessType: string | number;
56
+ /** Current business type display name (corresponding to businessType) */
57
+ businessTypeLabel?: string;
58
+ businessTypeApiKey?: string;
59
+ changesSince: string | number;
60
+ /** Changes Since Custom: selected date's midnight Unix millisecond timestamp */
61
+ changesSinceCustomTime?: number;
62
+ }
63
+
64
+ /** Designer / page-injected current logged-in user (consistent with __NeoCurrentUser) */
65
+ interface NeoCurrentUser {
66
+ id: number;
67
+ name: string;
68
+ icon?: string;
69
+ departId?: number;
70
+ localCode?: string;
71
+ type?: number;
72
+ languageCode?: string;
73
+ currencyApiKey?: string;
74
+ }
75
+
76
+ interface FilterBarProps {
77
+ /** Close Date dropdown options (passed in from page / designer configuration) */
78
+ closeDateOptions?: FilterOption[];
79
+ /**
80
+ * Default business type: when matching the apiKey of an item returned by the API, use that item's id (or value) as the initial Business Type selection
81
+ */
82
+ defaultBusiType?: string;
83
+ className?: string;
84
+ style?: React.CSSProperties;
85
+ /** Coexists with NeoEvent: allows parent component to listen directly */
86
+ onValuesChange?: (payload: FilterBarChangePayload) => void;
87
+ /** Page data: contains __NeoCurrentUser, used to insert current user at the top of the owner list */
88
+ data?: { __NeoCurrentUser?: NeoCurrentUser };
89
+ }
90
+
91
+ interface FilterValues {
92
+ closeDate: number | string;
93
+ businessType: string | number;
94
+ changesSince: string | number;
95
+ }
96
+
97
+ interface FilterBarState {
98
+ closeDateOptions: FilterOption[];
99
+ changesSinceOptions: FilterOption[];
100
+ opportunityOwnerOptions: FilterOption[];
101
+ businessTypeOptions: FilterOption[];
102
+ values: FilterValues;
103
+ /** Opportunity Owner selected personnel ids (multi-select) */
104
+ opportunityOwner: (number | string)[];
105
+ /** Custom date range timestamps when Close Date is 'custom' */
106
+ closeDateCustomRange: TimestampRange | null;
107
+ /** Single time point when Changes Since is Custom (midnight, Unix ms) */
108
+ changesSinceCustomTime: number | null;
109
+ ownerLoading: boolean;
110
+ businessTypeLoading: boolean;
111
+ }
112
+
113
+ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
114
+ /** Initialize filter state and Close Date options */
115
+ constructor(props: FilterBarProps) {
116
+ super(props);
117
+ const closeOpts = normalizeOptions(
118
+ props.closeDateOptions && props.closeDateOptions.length > 0
119
+ ? props.closeDateOptions
120
+ : FilterBar.defaultCloseDateOptions(),
121
+ );
122
+ const csOpts = defaultChangesSinceOptions();
123
+ const initialClose = closeOpts[2]?.value ?? 401; // Default: this quarter
124
+ const initialRelativeRange = relativeCloseDateRangeFromCode(initialClose);
125
+ this.state = {
126
+ closeDateOptions: closeOpts,
127
+ changesSinceOptions: csOpts,
128
+ opportunityOwnerOptions: [],
129
+ businessTypeOptions: [],
130
+ values: {
131
+ closeDate: initialClose,
132
+ businessType: '',
133
+ changesSince: csOpts[0]?.value ?? '',
134
+ },
135
+ opportunityOwner: getDefaultOpportunityOwnerIds(props),
136
+ closeDateCustomRange: initialRelativeRange,
137
+ changesSinceCustomTime: initialRelativeRange?.start ?? null,
138
+ ownerLoading: false,
139
+ businessTypeLoading: false,
140
+ };
141
+
142
+ // Bind methods to this
143
+ this.syncDefaultBusinessTypeFromProps =
144
+ this.syncDefaultBusinessTypeFromProps.bind(this);
145
+ this.syncCloseDateOptionsFromProps =
146
+ this.syncCloseDateOptionsFromProps.bind(this);
147
+ this.buildPayload = this.buildPayload.bind(this);
148
+ this.emitChange = this.emitChange.bind(this);
149
+ this.loadOpportunityOwnerOptions =
150
+ this.loadOpportunityOwnerOptions.bind(this);
151
+ this.loadBusinessTypeOptions = this.loadBusinessTypeOptions.bind(this);
152
+ this.patchValues = this.patchValues.bind(this);
153
+ this.handleCloseDateChange = this.handleCloseDateChange.bind(this);
154
+ this.handleChangesSinceChange = this.handleChangesSinceChange.bind(this);
155
+ this.handleCloseDateRangeChange =
156
+ this.handleCloseDateRangeChange.bind(this);
157
+ this.handleChangesSinceTimeChange =
158
+ this.handleChangesSinceTimeChange.bind(this);
159
+ this.closeDateRangePickerValue = this.closeDateRangePickerValue.bind(this);
160
+ this.changesSinceDatePickerValue =
161
+ this.changesSinceDatePickerValue.bind(this);
162
+ this.getFilters = this.getFilters.bind(this);
163
+ this.resetFilters = this.resetFilters.bind(this);
164
+ this.renderCloseDateBlock = this.renderCloseDateBlock.bind(this);
165
+ this.renderOwnerBlock = this.renderOwnerBlock.bind(this);
166
+ this.renderBusinessTypeBlock = this.renderBusinessTypeBlock.bind(this);
167
+ this.renderChangesSinceBlock = this.renderChangesSinceBlock.bind(this);
168
+ }
169
+
170
+ /** Close Date built-in default options */
171
+ static defaultCloseDateOptions(): FilterOption[] {
172
+ return [
173
+ { value: 201, label: 'This Week' },
174
+ { value: 301, label: 'This Month' },
175
+ { value: 401, label: 'This Quarter' },
176
+ { value: 'custom', label: 'Custom' },
177
+ ];
178
+ }
179
+
180
+ /** Sync props options and fetch owner and business type data */
181
+ componentDidMount() {
182
+ this.syncCloseDateOptionsFromProps(this.props);
183
+ this.loadOpportunityOwnerOptions();
184
+ this.loadBusinessTypeOptions();
185
+ }
186
+
187
+ /** Sync local options when props.closeDateOptions changes */
188
+ componentWillReceiveProps(nextProps: FilterBarProps) {
189
+ if (nextProps.closeDateOptions !== this.props.closeDateOptions) {
190
+ this.syncCloseDateOptionsFromProps(nextProps);
191
+ }
192
+ if (nextProps.defaultBusiType !== this.props.defaultBusiType) {
193
+ this.syncDefaultBusinessTypeFromProps(nextProps);
194
+ }
195
+ const nextOwnerIds = getDefaultOpportunityOwnerIds(nextProps);
196
+ const prevUserId = this.props.data?.__NeoCurrentUser?.id;
197
+ const nextUserId = nextProps.data?.__NeoCurrentUser?.id;
198
+ if (
199
+ nextOwnerIds.length > 0 &&
200
+ prevUserId !== nextUserId &&
201
+ this.state.opportunityOwner.length === 0
202
+ ) {
203
+ this.setState({ opportunityOwner: nextOwnerIds }, () =>
204
+ this.emitChange(),
205
+ );
206
+ }
207
+ }
208
+
209
+ /** Re-match default business type from loaded dropdown data when defaultBusiType config changes */
210
+ syncDefaultBusinessTypeFromProps(props: FilterBarProps) {
211
+ const { businessTypeOptions } = this.state;
212
+ if (!businessTypeOptions.length) return;
213
+ const nextVal = resolveDefaultBusinessTypeValue(
214
+ businessTypeOptions,
215
+ props.defaultBusiType,
216
+ );
217
+ this.setState(
218
+ (prev) => ({
219
+ values: { ...prev.values, businessType: nextVal },
220
+ }),
221
+ () => this.emitChange(),
222
+ );
223
+ }
224
+
225
+ /** Update Close Date options from props and correct current selected value */
226
+ syncCloseDateOptionsFromProps(props: FilterBarProps) {
227
+ const next = normalizeOptions(
228
+ props.closeDateOptions && props.closeDateOptions.length > 0
229
+ ? props.closeDateOptions
230
+ : FilterBar.defaultCloseDateOptions(),
231
+ );
232
+ this.setState((prev) => {
233
+ const stillValid = next.some((o) => o.value === prev.values.closeDate);
234
+ const closeDate = stillValid
235
+ ? prev.values.closeDate
236
+ : next[0]?.value ?? '';
237
+ const isCustom = String(closeDate).toLowerCase() === 'custom';
238
+ const relativeRange = !isCustom
239
+ ? relativeCloseDateRangeFromCode(closeDate)
240
+ : null;
241
+ return {
242
+ closeDateOptions: next,
243
+ values: { ...prev.values, closeDate },
244
+ closeDateCustomRange: isCustom
245
+ ? prev.closeDateCustomRange
246
+ : relativeRange,
247
+ changesSinceCustomTime:
248
+ prev.values.changesSince === 'Custom'
249
+ ? prev.changesSinceCustomTime
250
+ : relativeRange?.start ?? null,
251
+ };
252
+ });
253
+ }
254
+
255
+ /** Build payload from state for external events / callbacks */
256
+ buildPayload(): FilterBarChangePayload {
257
+ const {
258
+ values,
259
+ opportunityOwner,
260
+ closeDateCustomRange,
261
+ changesSinceCustomTime,
262
+ businessTypeOptions,
263
+ } = this.state;
264
+ const closeCustom = String(values.closeDate).toLowerCase() === 'custom';
265
+ const changesCustom = values.changesSince === 'Custom';
266
+
267
+ const rangeResolved = closeCustom
268
+ ? closeDateCustomRange
269
+ : closeDateCustomRange ??
270
+ relativeCloseDateRangeFromCode(values.closeDate);
271
+
272
+ const curBusinessType =
273
+ values.businessType === '' || values.businessType == null
274
+ ? undefined
275
+ : businessTypeOptions.find((o) => o.value === values.businessType);
276
+
277
+ return {
278
+ closeDate: values.closeDate,
279
+ closeDateCustomRange: rangeResolved ?? undefined,
280
+ opportunityOwner,
281
+ businessType: values.businessType,
282
+ businessTypeLabel: curBusinessType?.label,
283
+ businessTypeApiKey: curBusinessType?.apiKey,
284
+ changesSince: values.changesSince,
285
+ changesSinceCustomTime: changesCustom
286
+ ? changesSinceCustomTime ?? undefined
287
+ : rangeResolved?.start != null
288
+ ? rangeResolved.start
289
+ : undefined,
290
+ };
291
+ }
292
+
293
+ /** Log, trigger onValuesChange and designer event */
294
+ emitChange() {
295
+ const payload = this.buildPayload();
296
+ console.log('[FilterBar__c] filters change', payload);
297
+ const { onValuesChange } = this.props;
298
+ if (onValuesChange) {
299
+ onValuesChange(payload);
300
+ }
301
+ this.onFiltersChange({
302
+ data: payload,
303
+ });
304
+
305
+ /*
306
+ console.log('Triggered a broadcast event updateFilterData:', payload);
307
+ NeoEvent.broadcast('updateFilterData', payload);
308
+ */
309
+ }
310
+
311
+ /** Designer event: filter conditions changed (including custom date range) */
312
+ @NeoEvent.dispatch
313
+ onFiltersChange(eventData?: FilterBarEventData) {}
314
+
315
+ /** Fetch user entity to populate Opportunity Owner dropdown */
316
+ async loadOpportunityOwnerOptions() {
317
+ this.setState({ ownerLoading: true });
318
+ const currentUser = Object.assign({}, this.props.data?.__NeoCurrentUser);
319
+ // Add marker to current user
320
+ if (
321
+ currentUser &&
322
+ currentUser.name &&
323
+ currentUser.name.indexOf('(CurrentUser)') === -1
324
+ ) {
325
+ currentUser.name = currentUser.name + '(CurrentUser)';
326
+ }
327
+ const currentUserRecords =
328
+ currentUser?.id != null &&
329
+ currentUser.name != null &&
330
+ String(currentUser.name).trim() !== ''
331
+ ? [currentUser]
332
+ : [];
333
+
334
+ try {
335
+ const result = await xObject.query({
336
+ xObjectApiKey: USER_ENTITY_API_KEY,
337
+ fields: [...USER_QUERY_FIELDS],
338
+ page: 1,
339
+ pageSize: 500,
340
+ });
341
+
342
+ let opportunityOwnerOptions: FilterOption[] = [];
343
+ if (result?.status) {
344
+ const records = Array.isArray(result.data) ? result.data : [];
345
+ opportunityOwnerOptions = parseUserRecordsToOwnerOptions([
346
+ ...currentUserRecords,
347
+ ...records,
348
+ ]);
349
+ } else {
350
+ console.warn(
351
+ 'FilterBar xObject.query(user) not successful:',
352
+ result?.msg ?? result,
353
+ );
354
+ opportunityOwnerOptions =
355
+ parseUserRecordsToOwnerOptions(currentUserRecords);
356
+ }
357
+
358
+ const defaultOwnerIds = getDefaultOpportunityOwnerIds(this.props);
359
+ let appliedOwnerDefault = false;
360
+ this.setState(
361
+ (prev) => {
362
+ const shouldApply =
363
+ defaultOwnerIds.length > 0 && prev.opportunityOwner.length === 0;
364
+ if (shouldApply) appliedOwnerDefault = true;
365
+ return {
366
+ ownerLoading: false,
367
+ opportunityOwnerOptions,
368
+ opportunityOwner: shouldApply
369
+ ? defaultOwnerIds
370
+ : prev.opportunityOwner,
371
+ };
372
+ },
373
+ () => {
374
+ if (appliedOwnerDefault) this.emitChange();
375
+ },
376
+ );
377
+ } catch (e) {
378
+ console.error('FilterBar failed to load owner (user):', e);
379
+ const fallbackOptions =
380
+ parseUserRecordsToOwnerOptions(currentUserRecords);
381
+ const defaultOwnerIds = getDefaultOpportunityOwnerIds(this.props);
382
+ let appliedOwnerDefault = false;
383
+ this.setState(
384
+ (prev) => {
385
+ const shouldApply =
386
+ defaultOwnerIds.length > 0 && prev.opportunityOwner.length === 0;
387
+ if (shouldApply) appliedOwnerDefault = true;
388
+ return {
389
+ ownerLoading: false,
390
+ opportunityOwnerOptions: fallbackOptions,
391
+ opportunityOwner: shouldApply
392
+ ? defaultOwnerIds
393
+ : prev.opportunityOwner,
394
+ };
395
+ },
396
+ () => {
397
+ if (appliedOwnerDefault) this.emitChange();
398
+ },
399
+ );
400
+ }
401
+ }
402
+
403
+ /** Fetch opportunity business type API to populate Business Type dropdown */
404
+ async loadBusinessTypeOptions() {
405
+ this.setState({ businessTypeLoading: true });
406
+ try {
407
+ const res = await request({
408
+ url: BUSI_TYPE_URL,
409
+ method: 'GET',
410
+ data: {},
411
+ });
412
+ const businessTypeOptions = parseBusinessTypes(res);
413
+ const defaultBt = resolveDefaultBusinessTypeValue(
414
+ businessTypeOptions,
415
+ this.props.defaultBusiType,
416
+ );
417
+ this.setState(
418
+ (prev) => ({
419
+ businessTypeLoading: false,
420
+ businessTypeOptions,
421
+ values: {
422
+ ...prev.values,
423
+ businessType: defaultBt,
424
+ },
425
+ }),
426
+ () => {
427
+ if (defaultBt) this.emitChange();
428
+ },
429
+ );
430
+ } catch (e) {
431
+ console.error('FilterBar failed to load business types:', e);
432
+ this.setState({ businessTypeLoading: false, businessTypeOptions: [] });
433
+ }
434
+ }
435
+
436
+ /** Merge values subset and notify change */
437
+ patchValues(patch: Partial<FilterValues>) {
438
+ this.setState(
439
+ (prev) =>
440
+ ({
441
+ values: { ...prev.values, ...patch },
442
+ } as Pick<FilterBarState, 'values'>),
443
+ () => this.emitChange(),
444
+ );
445
+ }
446
+
447
+ /** Close Date dropdown change; for non-custom, write relative period start/end timestamps and start to changesSinceCustomTime */
448
+ handleCloseDateChange(value: number | string) {
449
+ if (String(value).toLowerCase() !== 'custom') {
450
+ const relativeRange = relativeCloseDateRangeFromCode(value);
451
+ this.setState(
452
+ (prev) =>
453
+ ({
454
+ values: { ...prev.values, closeDate: value },
455
+ closeDateCustomRange: relativeRange,
456
+ changesSinceCustomTime:
457
+ prev.values.changesSince === 'Custom'
458
+ ? prev.changesSinceCustomTime
459
+ : relativeRange?.start ?? null,
460
+ } as Pick<
461
+ FilterBarState,
462
+ 'values' | 'closeDateCustomRange' | 'changesSinceCustomTime'
463
+ >),
464
+ () => this.emitChange(),
465
+ );
466
+ return;
467
+ }
468
+ this.setState(
469
+ (prev) => ({
470
+ values: { ...prev.values, closeDate: value },
471
+ closeDateCustomRange: null,
472
+ changesSinceCustomTime:
473
+ prev.values.changesSince === 'Custom'
474
+ ? prev.changesSinceCustomTime
475
+ : null,
476
+ }),
477
+ () => this.emitChange(),
478
+ );
479
+ }
480
+
481
+ /** Changes Since dropdown change; for non-Custom with relative Close Date period, sync start timestamp */
482
+ handleChangesSinceChange(value: string | number) {
483
+ if (value !== 'Custom') {
484
+ const closeDate = this.state.values.closeDate;
485
+ const closeCustom = String(closeDate).toLowerCase() === 'custom';
486
+ const relativeRange = !closeCustom
487
+ ? relativeCloseDateRangeFromCode(closeDate)
488
+ : null;
489
+ this.setState(
490
+ (prev) =>
491
+ ({
492
+ values: { ...prev.values, changesSince: value },
493
+ changesSinceCustomTime: relativeRange?.start ?? null,
494
+ } as Pick<FilterBarState, 'values' | 'changesSinceCustomTime'>),
495
+ () => this.emitChange(),
496
+ );
497
+ return;
498
+ }
499
+ this.setState(
500
+ (prev) => ({
501
+ values: { ...prev.values, changesSince: value },
502
+ changesSinceCustomTime: null,
503
+ }),
504
+ () => this.emitChange(),
505
+ );
506
+ }
507
+
508
+ /** Close Date custom RangePicker: write timestamp range */
509
+ handleCloseDateRangeChange(dates: any) {
510
+ const closeDateCustomRange = momentRangeToTimestamps(dates);
511
+ this.setState({ closeDateCustomRange }, () => this.emitChange());
512
+ }
513
+
514
+ /** Changes Since custom DatePicker: write midnight timestamp */
515
+ handleChangesSinceTimeChange(date: Moment | null) {
516
+ const changesSinceCustomTime =
517
+ date != null ? moment(date).startOf('day').valueOf() : null;
518
+ this.setState({ changesSinceCustomTime }, () => this.emitChange());
519
+ }
520
+
521
+ /** state.closeDateCustomRange -> RangePicker controlled value */
522
+ closeDateRangePickerValue(): [Moment | null, Moment | null] | null {
523
+ const r = this.state.closeDateCustomRange;
524
+ if (!r || (r.start == null && r.end == null)) return null;
525
+ return [
526
+ r.start != null ? moment(r.start) : null,
527
+ r.end != null ? moment(r.end) : null,
528
+ ];
529
+ }
530
+
531
+ /** state.changesSinceCustomTime -> DatePicker controlled value */
532
+ changesSinceDatePickerValue(): Moment | null {
533
+ const t = this.state.changesSinceCustomTime;
534
+ if (t == null) return null;
535
+ return moment(t);
536
+ }
537
+
538
+ /** Action flow / external call: return current filter snapshot */
539
+ @NeoEvent.function
540
+ getFilters(): FilterBarChangePayload {
541
+ return this.buildPayload();
542
+ }
543
+
544
+ /** Action flow: reset to defaults and clear custom ranges, then notify */
545
+ @NeoEvent.function
546
+ resetFilters() {
547
+ const { closeDateOptions, changesSinceOptions } = this.state;
548
+ const resetClose = closeDateOptions[0]?.value ?? '';
549
+ const resetCustom = String(resetClose).toLowerCase() === 'custom';
550
+ const resetRange = !resetCustom
551
+ ? relativeCloseDateRangeFromCode(resetClose)
552
+ : null;
553
+ this.setState(
554
+ {
555
+ values: {
556
+ closeDate: resetClose,
557
+ businessType: '',
558
+ changesSince: changesSinceOptions[0]?.value ?? '',
559
+ },
560
+ opportunityOwner: getDefaultOpportunityOwnerIds(this.props),
561
+ closeDateCustomRange: resetRange,
562
+ changesSinceCustomTime: resetRange?.start ?? null,
563
+ },
564
+ () => this.emitChange(),
565
+ );
566
+ }
567
+
568
+ /** Close Date + custom date range */
569
+ renderCloseDateBlock() {
570
+ const { closeDateOptions, values } = this.state;
571
+ const showCustom = String(values.closeDate).toLowerCase() === 'custom';
572
+
573
+ return (
574
+ <div className="filter-block" key="closeDate">
575
+ <div className="filter-field">
576
+ <label>Close Date</label>
577
+ <Select
578
+ className="filter-select"
579
+ value={values.closeDate || undefined}
580
+ onChange={(v) => this.handleCloseDateChange(v)}
581
+ options={closeDateOptions.map((o) => ({
582
+ value: o.value,
583
+ label: o.label,
584
+ }))}
585
+ />
586
+ </div>
587
+ {showCustom && (
588
+ <div className="filter-field filter-field-range">
589
+ <RangePicker
590
+ value={this.closeDateRangePickerValue()}
591
+ onChange={this.handleCloseDateRangeChange}
592
+ format="YYYY-MM-DD"
593
+ />
594
+ </div>
595
+ )}
596
+ </div>
597
+ );
598
+ }
599
+
600
+ /** Opportunity Owner */
601
+ renderOwnerBlock() {
602
+ const { opportunityOwnerOptions, ownerLoading, opportunityOwner } =
603
+ this.state;
604
+
605
+ return (
606
+ <div className="filter-block" key="owner">
607
+ <div className="filter-field">
608
+ <label>Opportunity Owner</label>
609
+ <Spin spinning={ownerLoading}>
610
+ <div className="filter-select-spin-wrap">
611
+ <Select
612
+ className="filter-select filter-select-wide"
613
+ mode="multiple"
614
+ allowClear
615
+ showSearch
616
+ placeholder="Please select the owner"
617
+ value={opportunityOwner}
618
+ optionFilterProp="label"
619
+ maxTagCount={2}
620
+ maxTagPlaceholder={(omitted) => `+${omitted.length}`}
621
+ onChange={(v) =>
622
+ this.setState({ opportunityOwner: v ?? [] }, () =>
623
+ this.emitChange(),
624
+ )
625
+ }
626
+ dropdownMatchSelectWidth={320}
627
+ options={opportunityOwnerOptions.map((o) => ({
628
+ value: o.value,
629
+ label: o.label,
630
+ }))}
631
+ />
632
+ </div>
633
+ </Spin>
634
+ </div>
635
+ </div>
636
+ );
637
+ }
638
+
639
+ /** Business Type */
640
+ renderBusinessTypeBlock() {
641
+ const { businessTypeOptions, businessTypeLoading, values } = this.state;
642
+
643
+ return (
644
+ <div className="filter-block" key="businessType">
645
+ <div className="filter-field">
646
+ <label>Business Type</label>
647
+ <Spin spinning={businessTypeLoading}>
648
+ <div className="filter-select-spin-wrap">
649
+ <Select
650
+ className="filter-select"
651
+ allowClear
652
+ showSearch
653
+ placeholder="Please select the business type"
654
+ value={values.businessType || undefined}
655
+ optionFilterProp="label"
656
+ onChange={(v) => this.patchValues({ businessType: v || '' })}
657
+ options={businessTypeOptions.map((o) => ({
658
+ value: o.value,
659
+ label: o.label,
660
+ }))}
661
+ />
662
+ </div>
663
+ </Spin>
664
+ </div>
665
+ </div>
666
+ );
667
+ }
668
+
669
+ /** Changes Since + single date when Custom */
670
+ renderChangesSinceBlock() {
671
+ const { changesSinceOptions, values } = this.state;
672
+ const showCustom = values.changesSince === 'Custom';
673
+
674
+ return (
675
+ <div className="filter-block" key="changesSince">
676
+ <div className="filter-field">
677
+ <label>
678
+ Changes Since
679
+ <span className="help-tip">
680
+ ?
681
+ <span className="help-tip-text">
682
+ Changes Since custom: start time relative to the report period;
683
+ select a date when Custom
684
+ </span>
685
+ </span>
686
+ </label>
687
+ <Select
688
+ className="filter-select"
689
+ value={values.changesSince || undefined}
690
+ onChange={(v) => this.handleChangesSinceChange(v)}
691
+ options={changesSinceOptions.map((o) => ({
692
+ value: o.value,
693
+ label: o.label,
694
+ }))}
695
+ />
696
+ </div>
697
+ {showCustom && (
698
+ <div className="filter-field filter-field-range">
699
+ <DatePicker
700
+ value={this.changesSinceDatePickerValue()}
701
+ onChange={this.handleChangesSinceTimeChange}
702
+ format="YYYY-MM-DD"
703
+ placeholder="Select date"
704
+ />
705
+ </div>
706
+ )}
707
+ </div>
708
+ );
709
+ }
710
+
711
+ /** Filter bar root layout */
712
+ render() {
713
+ const { className, style } = this.props;
714
+ console.log('[FilterBar__c] render', this.props);
715
+
716
+ return (
717
+ <div
718
+ className={`filterBar__c ${className || ''}`}
719
+ style={style}
720
+ data-time="2026.4.17 01"
721
+ >
722
+ {this.renderCloseDateBlock()}
723
+ {this.renderOwnerBlock()}
724
+ {this.renderBusinessTypeBlock()}
725
+ {this.renderChangesSinceBlock()}
726
+ </div>
727
+ );
728
+ }
729
+ }
730
+
731
+ export default FilterBar;