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,229 @@
1
+ /**
2
+ * Shared pure utility methods across multiple components (filter where, stage name, amount display, NeoBI request body, etc.)
3
+ */
4
+
5
+ export interface PropsContext {
6
+ data?: {
7
+ __NeoCurrentUser?: {
8
+ id?: number;
9
+ name?: string | null;
10
+ };
11
+ };
12
+ }
13
+ /** Convert filter option to finite number, returns null if invalid (consistent with filter2chartFilter, etc.) */
14
+ export function toFiniteNumber(v: unknown): number | null {
15
+ if (v == null || v === '') return null;
16
+ if (typeof v === 'number' && Number.isFinite(v)) return v;
17
+ const n = Number(v);
18
+ return Number.isFinite(n) ? n : null;
19
+ }
20
+
21
+ /** Time range start/end: returns closed interval [start, end] when both are valid numbers (millisecond timestamps, consistent with FilterBar) */
22
+ export function closeRangeFromFilter(
23
+ range: unknown,
24
+ ): { start: number; end: number } | null {
25
+ if (!range || typeof range !== 'object') return null;
26
+ const r = range as { start?: unknown; end?: unknown };
27
+ const start = toFiniteNumber(r.start);
28
+ const end = toFiniteNumber(r.end);
29
+ if (start == null || end == null) return null;
30
+ return { start, end };
31
+ }
32
+
33
+ /** opportunityOwner -> user id list for where clause (string to avoid big integer precision loss) */
34
+ export function normalizeOwnerIdsForWhere(owner: unknown): string[] {
35
+ if (owner == null || owner === '') return [];
36
+ const raw = Array.isArray(owner) ? owner : [owner];
37
+ const out: string[] = [];
38
+ for (const item of raw) {
39
+ if (item == null || item === '') continue;
40
+ const s = String(item).trim().replace(/,/g, '');
41
+ if (s === '' || !/^-?\d+$/.test(s)) continue;
42
+ out.push(s);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ export function entityTypeIdForWhere(businessType: unknown): string | null {
48
+ if (businessType == null || businessType === '') return null;
49
+ const s = String(businessType).trim();
50
+ if (s === '' || !/^-?\d+$/.test(s.replace(/,/g, ''))) return null;
51
+ return s.replace(/,/g, '');
52
+ }
53
+
54
+ /** Extract the segment after the first '.' from stage display name, used for matching customItem1__c, etc. */
55
+ export function extractStageKeyFromStageName(stageName: string): string {
56
+ const s = String(stageName ?? '').trim();
57
+ const i = s.indexOf('.');
58
+ if (i < 0) return s;
59
+ return s.slice(i + 1).trim();
60
+ }
61
+
62
+ /** Opportunity rows, etc.: returns 0 when empty or unparseable */
63
+ export function parseNumberOrZero(raw: unknown): number {
64
+ if (raw === null || raw === undefined || raw === '') return 0;
65
+ const n =
66
+ typeof raw === 'number'
67
+ ? raw
68
+ : parseFloat(String(raw).replace(/,/g, '').trim());
69
+ return Number.isFinite(n) ? n : 0;
70
+ }
71
+
72
+ /** Threshold config, etc.: returns NaN when empty or unparseable */
73
+ export function parseNumberOrNaN(raw: unknown): number {
74
+ if (raw === null || raw === undefined || raw === '') return NaN;
75
+ const n =
76
+ typeof raw === 'number'
77
+ ? raw
78
+ : parseFloat(String(raw).replace(/,/g, '').trim());
79
+ return Number.isFinite(n) ? n : NaN;
80
+ }
81
+
82
+ /** Display amount: $ prefix; uses K / M / B / T abbreviation when >= 1e3 */
83
+ export function formatAmountDisplay(n: number): string {
84
+ if (!Number.isFinite(n)) return '-';
85
+ const sign = n < 0 ? '-' : '';
86
+ const abs = Math.abs(n);
87
+ if (abs === 0) return '$0';
88
+
89
+ if (abs < 1000) {
90
+ const part = Number.isInteger(abs)
91
+ ? abs.toLocaleString('en-US')
92
+ : abs.toLocaleString('en-US', {
93
+ minimumFractionDigits: 0,
94
+ maximumFractionDigits: 2,
95
+ });
96
+ return `${sign}$${part}`;
97
+ }
98
+
99
+ const tier = Math.min(Math.floor(Math.log10(abs) / 3), 4);
100
+ const suffixes = ['', 'K', 'M', 'B', 'T'] as const;
101
+ const suffix = suffixes[tier];
102
+ const divisor = 1000 ** tier;
103
+ const scaled = abs / divisor;
104
+
105
+ let body: string;
106
+ if (scaled >= 100) body = String(Math.round(scaled));
107
+ else if (scaled >= 10) body = scaled.toFixed(1).replace(/\.0$/, '');
108
+ else body = scaled.toFixed(2).replace(/\.?0+$/, '');
109
+
110
+ return `${sign}$${body}${suffix}`;
111
+ }
112
+
113
+ /** queryDataTask: x-www-form-urlencoded form body (filter passed as JSON string) */
114
+ export function buildQueryDataTaskFormBody(params: {
115
+ viewId: string;
116
+ userId: string | number;
117
+ type: string;
118
+ filter: Record<string, unknown>;
119
+ }): string {
120
+ const form = new URLSearchParams();
121
+ form.set('viewId', String(params.viewId));
122
+ form.set('userId', String(params.userId));
123
+ form.set('type', params.type);
124
+ form.set('filter', JSON.stringify(params.filter));
125
+ return form.toString();
126
+ }
127
+
128
+ /** Owner multi-select default: current logged-in user id (consistent with __NeoCurrentUser) */
129
+ export function getDefaultOpportunityOwnerIds(
130
+ props: PropsContext,
131
+ ): (number | string)[] {
132
+ // return [1246045]; // Default user changed to (Alice)
133
+ const curUser = props.data?.__NeoCurrentUser;
134
+ if (
135
+ curUser?.id == null ||
136
+ curUser.name == null ||
137
+ String(curUser.name).trim() === ''
138
+ ) {
139
+ return [];
140
+ }
141
+ return [curUser.id];
142
+ }
143
+
144
+ /** Owner multi-select default: current logged-in user id (consistent with __NeoCurrentUser) */
145
+ export function getDefaultFilterWhereByProps(props: any = {}): any {
146
+ let userId = 1246045; // Default user (Alice)
147
+ const curUser = props.data?.__NeoCurrentUser;
148
+ if (curUser?.id) {
149
+ userId = curUser.id;
150
+ }
151
+ const defaultFilter = {
152
+ relation: '1 and 2 and 3',
153
+ filter: [
154
+ {
155
+ modelKey: 'opportunity',
156
+ serialNo: 1,
157
+ fieldKey: 'closeDate',
158
+ type: 'date',
159
+ entityOnlyFilter: 0,
160
+ timeType: 'relative',
161
+ operate: 'eq',
162
+ value: [
163
+ {
164
+ val: 401,
165
+ baseType: 'system',
166
+ },
167
+ ],
168
+ seq: 1,
169
+ },
170
+ {
171
+ modelKey: 'opportunity',
172
+ serialNo: 1,
173
+ fieldKey: 'ownerId',
174
+ type: 'refer',
175
+ referKey: 'user',
176
+ optionType: 'user',
177
+ entityOnlyFilter: 0,
178
+ operate: 'in',
179
+ value: [
180
+ {
181
+ val: userId,
182
+ sub: false,
183
+ source: 'internal',
184
+ specialType: 0,
185
+ },
186
+ ],
187
+ includeNullValue: 0,
188
+ seq: 2,
189
+ },
190
+ {
191
+ modelKey: 'opportunity',
192
+ serialNo: 1,
193
+ fieldKey: 'entityType',
194
+ type: 'refer',
195
+ entityOnlyFilter: 0,
196
+ operate: 'in',
197
+ value: [
198
+ {
199
+ val: 8150459,
200
+ },
201
+ ],
202
+ seq: 3,
203
+ },
204
+ ],
205
+ };
206
+ return defaultFilter;
207
+ }
208
+
209
+ export function getDefaultFilterByProps(props: any = {}): any {
210
+ let userId = 1246045; // Default user (Alice)
211
+ const curUser = props.data?.__NeoCurrentUser;
212
+ if (curUser?.id) {
213
+ userId = curUser.id;
214
+ }
215
+ const defaultFilter = {
216
+ closeDate: 401,
217
+ closeDateCustomRange: {
218
+ start: 1775059200000,
219
+ end: 1782835199999,
220
+ },
221
+ opportunityOwner: [userId],
222
+ businessType: 8150459,
223
+ businessTypeLabel: 'New Business',
224
+ businessTypeApiKey: 'defaultBusiType_1',
225
+ changesSince: 'Start of the Period',
226
+ changesSinceCustomTime: 1775059200000,
227
+ };
228
+ return defaultFilter;
229
+ }
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Filter condition formatting
3
+ * Formats the filter conditions from the filter bar component into the format required by the API.
4
+ *
5
+ * Data conversion rules:
6
+ 1. closeDate
7
+
8
+ Non-custom: Use toFiniteNumber to convert the option value (e.g. 201 / "201") to a number, timeType: 'relative', value: [{ val, baseType: 'system' }].
9
+ custom: Read closeDateCustomRange.start / end; when both are valid numbers, timeType: 'absolute', value is two { val, close: 1 } entries; if range is missing, this item is not generated.
10
+
11
+ 2. opportunityOwner -> ownerId
12
+ Supports single value (string/number) or multi-select array; toFiniteNumber each item, invalid items are discarded. When at least one valid id exists, generates a refer condition (referKey: 'user', optionType: 'user', operate: 'in', includeNullValue: 0).
13
+ value is an array, each user is { val, sub: false, source: 'internal', specialType: 0 }.
14
+
15
+ 3. businessType -> entityType
16
+ When a value exists and can be converted to a number, generates refer + value: [{ val }]; empty string does not pass entityType.
17
+
18
+ 4. relation
19
+ Concatenated as "1 and 2 and 3" based on the seq of final items; when there are no conditions, it is '', filter is [].
20
+
21
+ *
22
+ * Example:
23
+ * Filter conditions from the filter bar component:
24
+ {
25
+ "closeDate": "custom",
26
+ "closeDateCustomRange": {
27
+ "start": 1775577600000,
28
+ "end": 1777996799999
29
+ },
30
+ "opportunityOwner": ["1255807", 2088134315786370],
31
+ "businessType": "8150459",
32
+ "changesSince": "Custom",
33
+ "changesSinceCustomTime": 1776268800000
34
+ }
35
+
36
+ Format required by the API:
37
+ {
38
+ "relation": "1 and 2 and 3",
39
+ "filter": [
40
+ {
41
+ "seq": 1,
42
+ "fieldKey": "closeDate",
43
+ "modelKey": "opportunity",
44
+ "serialNo": 1,
45
+ "type": "date",
46
+ "entityOnlyFilter": 0,
47
+ "timeType": "relative",
48
+ "operate": "eq",
49
+ "value": [
50
+ {
51
+ "val": 101,
52
+ "baseType": "system"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "seq": 2,
58
+ "fieldKey": "ownerId",
59
+ "modelKey": "opportunity",
60
+ "serialNo": 1,
61
+ "type": "refer",
62
+ "referKey": "user",
63
+ "optionType": "user",
64
+ "entityOnlyFilter": 0,
65
+ "operate": "in",
66
+ "value": [
67
+ {
68
+ "val": 1255807,
69
+ "sub": false,
70
+ "source": "internal",
71
+ "specialType": 0
72
+ },
73
+ {
74
+ "val": 2088134315786370,
75
+ "sub": false,
76
+ "source": "internal",
77
+ "specialType": 0
78
+ }
79
+ ],
80
+ "includeNullValue": 0
81
+ },
82
+ {
83
+ "seq": 3,
84
+ "fieldKey": "entityType",
85
+ "modelKey": "opportunity",
86
+ "serialNo": 1,
87
+ "type": "refer",
88
+ "entityOnlyFilter": 0,
89
+ "operate": "in",
90
+ "value": [
91
+ {
92
+ "val": 8150459
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+
99
+ If closeDate is custom, the closeDateCustomRange needs to be formatted into the API-required format
100
+ {
101
+ "seq": 3,
102
+ "fieldKey": "closeDate",
103
+ "modelKey": "opportunity",
104
+ "serialNo": 1,
105
+ "type": "date",
106
+ "entityOnlyFilter": 0,
107
+ "timeType": "absolute",
108
+ "operate": "eq",
109
+ "value": [
110
+ {
111
+ "val": 1777305600000,
112
+ "close": 1
113
+ },
114
+ {
115
+ "val": 1780156799999,
116
+ "close": 1
117
+ }
118
+ ]
119
+ }
120
+ */
121
+
122
+ import { toFiniteNumber } from './common';
123
+
124
+ const MODEL_KEY = 'opportunity';
125
+ const SERIAL_NO = 1;
126
+
127
+ type ChartFilterItem = {
128
+ seq: number;
129
+ fieldKey: string;
130
+ modelKey: string;
131
+ serialNo: number;
132
+ type: string;
133
+ entityOnlyFilter: number;
134
+ operate: string;
135
+ value: unknown[];
136
+ timeType?: string;
137
+ referKey?: string;
138
+ optionType?: string;
139
+ includeNullValue?: number;
140
+ };
141
+
142
+ export interface ChartFilterPayload {
143
+ relation: string;
144
+ filter: ChartFilterItem[];
145
+ }
146
+
147
+ function closeDateChunk(
148
+ filter: Record<string, unknown>,
149
+ ): Omit<ChartFilterItem, 'seq'> | null {
150
+ const raw = filter.closeDate;
151
+ const isCustom = String(raw ?? '').toLowerCase() === 'custom';
152
+
153
+ if (isCustom) {
154
+ const range = filter.closeDateCustomRange as
155
+ | { start?: unknown; end?: unknown }
156
+ | undefined;
157
+ const start = toFiniteNumber(range?.start);
158
+ const end = toFiniteNumber(range?.end);
159
+ if (start == null || end == null) return null;
160
+ return {
161
+ modelKey: MODEL_KEY,
162
+ serialNo: SERIAL_NO,
163
+ fieldKey: 'closeDate',
164
+ type: 'date',
165
+ entityOnlyFilter: 0,
166
+ timeType: 'absolute',
167
+ operate: 'eq',
168
+ value: [
169
+ { val: start, close: 1 },
170
+ { val: end, close: 1 },
171
+ ],
172
+ };
173
+ }
174
+
175
+ const n = toFiniteNumber(raw);
176
+ if (n == null) return null;
177
+ return {
178
+ modelKey: MODEL_KEY,
179
+ serialNo: SERIAL_NO,
180
+ fieldKey: 'closeDate',
181
+ type: 'date',
182
+ entityOnlyFilter: 0,
183
+ timeType: 'relative',
184
+ operate: 'eq',
185
+ value: [{ val: n, baseType: 'system' }],
186
+ };
187
+ }
188
+
189
+ const ownerUserValueItem = (val: number) => ({
190
+ val,
191
+ sub: false as const,
192
+ source: 'internal' as const,
193
+ specialType: 0,
194
+ });
195
+
196
+ /** Parse valid user id list from opportunityOwner (single value or id array) */
197
+ function normalizeOwnerIds(owner: unknown): number[] {
198
+ if (owner == null || owner === '') return [];
199
+ const raw = Array.isArray(owner) ? owner : [owner];
200
+ const out: number[] = [];
201
+ for (const item of raw) {
202
+ if (item == null || item === '') continue;
203
+ const n = toFiniteNumber(item);
204
+ if (n != null) out.push(n);
205
+ }
206
+ return out;
207
+ }
208
+
209
+ function ownerChunk(
210
+ filter: Record<string, unknown>,
211
+ ): Omit<ChartFilterItem, 'seq'> | null {
212
+ const ids = normalizeOwnerIds(filter.opportunityOwner);
213
+ if (ids.length === 0) return null;
214
+ return {
215
+ modelKey: MODEL_KEY,
216
+ serialNo: SERIAL_NO,
217
+ fieldKey: 'ownerId',
218
+ type: 'refer',
219
+ referKey: 'user',
220
+ optionType: 'user',
221
+ entityOnlyFilter: 0,
222
+ operate: 'in',
223
+ value: ids.map(ownerUserValueItem),
224
+ includeNullValue: 0,
225
+ };
226
+ }
227
+
228
+ function entityTypeChunk(
229
+ filter: Record<string, unknown>,
230
+ ): Omit<ChartFilterItem, 'seq'> | null {
231
+ const busi = filter.businessType;
232
+ if (busi == null || busi === '') return null;
233
+ const val = toFiniteNumber(busi);
234
+ if (val == null) return null;
235
+ return {
236
+ modelKey: MODEL_KEY,
237
+ serialNo: SERIAL_NO,
238
+ fieldKey: 'entityType',
239
+ type: 'refer',
240
+ entityOnlyFilter: 0,
241
+ operate: 'in',
242
+ value: [{ val }],
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Convert filter bar payload to chart/API filter structure; only maps closeDate, ownerId, entityType.
248
+ */
249
+ export const filter2chartFilter = (filter: any): ChartFilterPayload => {
250
+ const f = filter && typeof filter === 'object' ? filter : {};
251
+ const parts = [closeDateChunk(f), ownerChunk(f), entityTypeChunk(f)].filter(
252
+ Boolean,
253
+ ) as Omit<ChartFilterItem, 'seq'>[];
254
+
255
+ const filterOut: ChartFilterItem[] = parts.map((p, i) => ({
256
+ ...p,
257
+ seq: i + 1,
258
+ }));
259
+
260
+ const relation =
261
+ filterOut.length === 0
262
+ ? ''
263
+ : filterOut.map((x) => String(x.seq)).join(' and ');
264
+
265
+ return { relation, filter: filterOut };
266
+ };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Filter bar filterBar specific utility methods
3
+ */
4
+ import moment from 'moment';
5
+
6
+ export interface FilterOption {
7
+ value: string | number;
8
+ label: string;
9
+ /** apiKey on API items like business type, used to align with defaultBusiType config */
10
+ apiKey?: string;
11
+ }
12
+
13
+ /** Custom date range: start/end are Unix millisecond timestamps (day is local startOfDay / endOfDay) */
14
+ export interface TimestampRange {
15
+ start?: number;
16
+ end?: number;
17
+ }
18
+
19
+ /** Normalize dropdown item value / label */
20
+ export function normalizeOptions(list?: FilterOption[]): FilterOption[] {
21
+ if (!Array.isArray(list) || list.length === 0) return [];
22
+ return list.map((o) => ({
23
+ value: o.value,
24
+ label: String(o.label ?? o.value),
25
+ }));
26
+ }
27
+
28
+ /** Changes Since dropdown default options */
29
+ export function defaultChangesSinceOptions(): FilterOption[] {
30
+ return [
31
+ { value: 'Start of the Period', label: 'Start of the Period' },
32
+ { value: 'Custom', label: 'Custom' },
33
+ ];
34
+ }
35
+
36
+ /** Convert two selected days from RangePicker to start/end timestamps (inclusive of the day) */
37
+ export function momentRangeToTimestamps(dates: any): TimestampRange | null {
38
+ const m0 = dates?.[0];
39
+ const m1 = dates?.[1];
40
+ if (!m0 || !m1) return null;
41
+ return {
42
+ start: moment(m0).startOf('day').valueOf(),
43
+ end: moment(m1).endOf('day').valueOf(),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Convert Close Date relative period code to local calendar day start/end timestamps (start at midnight, end at 23:59:59.999).
49
+ * Convention: hundreds digit 2=this week (ISO week), 3=this month, 4=this quarter (e.g. 201/301/401 and other codes in the same range).
50
+ */
51
+ export function relativeCloseDateRangeFromCode(
52
+ encoded: number | string,
53
+ ): TimestampRange | null {
54
+ const curEncoded = typeof encoded === 'number' ? encoded : Number(encoded);
55
+ if (!Number.isFinite(curEncoded)) return null;
56
+ const now = moment();
57
+ if (curEncoded === 201) {
58
+ // This Week
59
+ return {
60
+ start: now.clone().startOf('isoWeek').startOf('day').valueOf(),
61
+ end: now.clone().endOf('isoWeek').endOf('day').valueOf(),
62
+ };
63
+ }
64
+ if (curEncoded === 301) {
65
+ // This Month (start is the second day of the month at midnight)
66
+ return {
67
+ start: now
68
+ .clone()
69
+ .startOf('month')
70
+ .startOf('day')
71
+ .add(1, 'day')
72
+ .valueOf(),
73
+ end: now.clone().endOf('month').endOf('day').valueOf(),
74
+ };
75
+ }
76
+ if (curEncoded === 401) {
77
+ // This Quarter (start is the second day of the quarter at midnight)
78
+ return {
79
+ start: now
80
+ .clone()
81
+ .startOf('quarter')
82
+ .startOf('day')
83
+ .add(1, 'day')
84
+ .valueOf(),
85
+ end: now.clone().endOf('quarter').endOf('day').valueOf(),
86
+ };
87
+ }
88
+ return null;
89
+ }
90
+
91
+ /** Convert user entity query results to owner dropdown options (deduplicated by id) */
92
+ export function parseUserRecordsToOwnerOptions(records: any): FilterOption[] {
93
+ const arr = Array.isArray(records) ? records : [];
94
+ const seen = new Set<string>();
95
+ const out: FilterOption[] = [];
96
+ for (const row of arr) {
97
+ if (row == null || typeof row !== 'object') continue;
98
+ const id = row.id;
99
+ const name = row.name != null ? String(row.name) : '';
100
+ if (!id || !name || seen.has(id)) continue;
101
+ seen.add(id);
102
+ out.push({ value: id, label: name });
103
+ }
104
+ return out;
105
+ }
106
+
107
+ /** Extract list array from business type API response */
108
+ export function getBusinessTypeRawList(res: any): any[] {
109
+ const raw = res?.data !== undefined ? res.data : res;
110
+ const arr = Array.isArray(raw)
111
+ ? raw
112
+ : raw?.records || raw?.items || raw?.list || raw?.data || [];
113
+ return Array.isArray(arr) ? arr : [];
114
+ }
115
+
116
+ /** Parse business type API results to dropdown options (preserving apiKey for default item matching) */
117
+ export function parseBusinessTypes(res: any): FilterOption[] {
118
+ const arr = getBusinessTypeRawList(res);
119
+ return arr
120
+ .map((x: any) => ({
121
+ value: x.id ?? x.apiKey ?? x.value,
122
+ label: String(x.label ?? x.name ?? x.title ?? x.apiKey ?? ''),
123
+ apiKey: x.apiKey != null ? String(x.apiKey) : undefined,
124
+ }))
125
+ .filter((o) => o.value != null && o.value !== '' && o.label);
126
+ }
127
+
128
+ /**
129
+ * Resolve default selected value in business type options by apiKey === defaultBusiType (consistent with dropdown value, usually id)
130
+ */
131
+ export function resolveDefaultBusinessTypeValue(
132
+ options: FilterOption[],
133
+ defaultBusiTypeKey?: string,
134
+ ): string | number {
135
+ if (defaultBusiTypeKey == null || defaultBusiTypeKey === '') return '';
136
+ const key = String(defaultBusiTypeKey);
137
+ const hit = options.find((o) => o.apiKey != null && o.apiKey === key);
138
+ if (!hit) return '';
139
+ return hit.value;
140
+ }