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.
- package/README.md +2 -1
- package/dist/index2.js +1 -1
- package/dist/main2.js +1 -1
- package/dist/neo/env.js +1 -1
- package/dist/neo/neoLogin.js +1 -1
- package/dist/neo/pushCmp.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +3 -2
- package/template/antd-custom-cmp-template/package.json +1 -1
- package/template/asset-manage-template/docs/README.md +1 -232
- package/template/asset-manage-template/package.json +2 -2
- package/template/echarts-custom-cmp-template/package.json +1 -1
- package/template/empty-custom-cmp-template/package.json +2 -2
- package/template/map-custom-cmp-template/package.json +1 -1
- package/template/neo-bi-cmps/neo.config.js +7 -1
- package/template/neo-bi-cmps/package.json +8 -7
- package/template/neo-bi-cmps/public/403.html +77 -0
- package/template/neo-bi-cmps/public/demo.html +2453 -0
- package/template/neo-bi-cmps/src/assets/icon/barChart.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/card.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/filter.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/funnel.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/tab.svg +1 -0
- package/template/neo-bi-cmps/src/components/filterBar__c/README.md +3 -14
- package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +29 -0
- package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +668 -146
- package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +26 -48
- package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +46 -139
- package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +11 -10
- package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +9 -16
- package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
- package/template/neo-bi-cmps/src/utils/common.ts +231 -0
- package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +268 -0
- package/template/neo-bi-cmps/src/utils/filterBar.ts +140 -0
- package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +341 -0
- package/template/{neo-h5-cmps/src/utils/queryObjectData.ts → neo-bi-cmps/src/utils/queryByCustomSQL.ts} +18 -13
- package/template/neo-bi-cmps/src/utils/requestDebounce.ts +22 -0
- package/template/neo-bi-cmps/src/utils/simpleTable.tsx +344 -0
- package/template/neo-bi-cmps/src/utils/stageSwitch.ts +15 -0
- package/template/neo-bi-cmps/src/utils/stageTimeChart.ts +90 -0
- package/template/neo-bi-cmps/src/utils/targetNumber.ts +12 -0
- package/template/neo-custom-cmp-template/docs/README.md +0 -231
- package/template/neo-custom-cmp-template/package.json +2 -2
- package/template/neo-h5-cmps/package.json +2 -2
- package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +1 -2
- package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +1 -1
- package/template/neo-h5-cmps/src/components/globalSearchInput__c/index.tsx +1 -1
- package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +1 -2
- package/template/neo-order-cmps/package.json +2 -2
- package/template/neo-pipeline-cmps/.prettierrc.js +12 -0
- package/template/neo-pipeline-cmps/@types/neo-ui-common.d.ts +36 -0
- package/template/neo-pipeline-cmps/README.md +99 -0
- package/template/neo-pipeline-cmps/commitlint.config.js +59 -0
- package/template/neo-pipeline-cmps/neo.config.js +135 -0
- package/template/neo-pipeline-cmps/package.json +66 -0
- package/template/neo-pipeline-cmps/public/403.html +77 -0
- package/template/neo-pipeline-cmps/public/css/base.css +283 -0
- package/template/neo-pipeline-cmps/public/demo.html +2453 -0
- package/template/neo-pipeline-cmps/public/scripts/app/bluebird.js +6679 -0
- package/template/neo-pipeline-cmps/public/template.html +13 -0
- package/template/neo-pipeline-cmps/src/assets/css/common.scss +127 -0
- package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +47 -0
- package/template/neo-pipeline-cmps/src/assets/icon/barChart.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/card.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/filter.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/funnel.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/tab.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/AIBtn.gif +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/NeoCRM.jpg +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/aiLogo.png +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/card-list.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/contact-form.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/custom-form.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/custom-widget.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/data-list.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/detail.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/favicon.png +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/map.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/search.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/table.svg +1 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +24 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +29 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +731 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +52 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +39 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +416 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +80 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/style.scss +83 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +470 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +45 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/style.scss +137 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +89 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +195 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +667 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +124 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +192 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +36 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +513 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +71 -0
- package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/style.scss +4 -2
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +37 -0
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +455 -0
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +106 -0
- package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/style.scss +3 -2
- package/template/neo-pipeline-cmps/src/utils/common.ts +229 -0
- package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +266 -0
- package/template/neo-pipeline-cmps/src/utils/filterBar.ts +140 -0
- package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +343 -0
- package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +121 -0
- package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
- package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +349 -0
- package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +15 -0
- package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +90 -0
- package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +12 -0
- package/template/neo-pipeline-cmps/tsconfig.json +40 -0
- package/template/neo-web-entity-grid/package.json +2 -2
- package/template/neo-web-form/package.json +2 -2
- package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +161 -41
- package/template/neo-web-form/src/components/batchAddTable__c/model.ts +4 -2
- package/template/react-custom-cmp-template/package.json +1 -1
- package/template/react-ts-custom-cmp-template/package.json +1 -1
- package/template/vue2-custom-cmp-template/package.json +1 -1
- package/template/asset-manage-template/src/utils/axiosFetcher.ts +0 -37
- package/template/asset-manage-template/src/utils/queryObjectData.ts +0 -112
- package/template/asset-manage-template/src/utils/xobjects.ts +0 -162
- package/template/neo-bi-cmps/.npmrc copy +0 -1
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +0 -52
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +0 -183
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +0 -90
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +0 -218
- package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +0 -31
- package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +0 -158
- package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +0 -40
- package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +0 -154
- package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +0 -36
- package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +0 -86
- package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +0 -62
- package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +0 -48
- package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +0 -24
- package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +0 -100
- package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +0 -46
- package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +0 -60
- package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +0 -35
- package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +0 -70
- package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +0 -50
- package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +0 -33
- package/template/neo-bi-cmps/src/components/oppList__c/README.md +0 -52
- package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +0 -285
- package/template/neo-bi-cmps/src/components/oppList__c/model.ts +0 -86
- package/template/neo-bi-cmps/src/components/oppList__c/style.scss +0 -133
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +0 -39
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +0 -130
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +0 -66
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +0 -133
- package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +0 -36
- package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +0 -118
- package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +0 -92
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +0 -37
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +0 -126
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +0 -57
- package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +0 -37
- package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +0 -80
- package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +0 -45
- package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +0 -37
- package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-bi-cmps/src/utils/queryObjectData.ts +0 -76
- package/template/neo-bi-cmps/src/utils/xobjects.ts +0 -162
- package/template/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -112
- package/template/neo-custom-cmp-template/src/utils/xobjects.ts +0 -162
- package/template/neo-h5-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-h5-cmps/src/utils/xobjects.ts +0 -167
- package/template/neo-order-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-order-cmps/src/utils/queryObjectData.ts +0 -112
- package/template/neo-order-cmps/src/utils/xobjects.ts +0 -162
- package/template/neo-web-entity-grid/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-web-entity-grid/src/utils/queryObjectData.ts +0 -112
- package/template/neo-web-entity-grid/src/utils/xobjects.ts +0 -167
- package/template/neo-web-form/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-web-form/src/utils/queryObjectData.ts +0 -112
- 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
|
+
}
|