neo-cmp-cli 1.13.17 → 1.13.19
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 +23 -3
- package/dist/index2.js +1 -1
- package/dist/neo/env.js +1 -1
- package/dist/neo/pushCmp.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +3 -2
- package/template/asset-manage-template/docs/README.md +1 -232
- package/template/echarts-custom-cmp-template/package.json +1 -1
- package/template/neo-bi-cmps/package.json +1 -1
- package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +1 -1
- package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +18 -10
- package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +8 -2
- package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
- package/template/neo-bi-cmps/src/utils/common.ts +18 -20
- package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +4 -6
- package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +4 -2
- package/template/neo-bi-cmps/src/utils/simpleTable.tsx +21 -16
- package/template/neo-custom-cmp-template/docs/README.md +0 -231
- package/template/neo-custom-cmp-template/package.json +1 -1
- 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-pipeline-cmps/neo.config.js +11 -0
- package/template/neo-pipeline-cmps/src/assets/css/common.scss +16 -16
- package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +9 -9
- package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +47 -46
- package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +22 -12
- package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +1 -1
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +17 -17
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +24 -22
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +31 -18
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/reset.scss +4 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +33 -26
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +9 -9
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +53 -54
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +70 -68
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +41 -41
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +2 -3
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +15 -15
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +35 -33
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +29 -16
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +18 -18
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +20 -20
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +34 -19
- package/template/neo-pipeline-cmps/src/utils/common.ts +14 -14
- package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +21 -23
- package/template/neo-pipeline-cmps/src/utils/filterBar.ts +14 -14
- package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +5 -5
- package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +26 -22
- package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +3 -3
- package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +31 -26
- package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +1 -1
- package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +5 -5
- package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +2 -2
- package/template/neo-web-entity-grid/src/components/createForm__c/index.tsx +271 -259
- package/template/neo-web-entity-grid/src/components/createForm__c/model.ts +17 -3
- package/template/neo-web-entity-grid/src/components/createForm__c/resetAntd.scss +0 -1
- package/template/neo-web-entity-grid/src/components/createForm__c/style.scss +1 -1
- package/template/neo-web-entity-grid/src/components/entityGrid2__c/index.tsx +5 -1
- package/template/neo-web-entity-grid/src/components/entityGrid2__c/model.ts +4 -3
- package/template/neo-web-entity-grid/src/components/entityGrid3__c/index.tsx +1 -1
- package/template/neo-web-entity-grid/src/components/searchForm__c/index.tsx +4 -3
- package/template/neo-web-entity-grid/src/components/searchForm__c/model.ts +9 -4
- package/template/neo-web-entity-grid/src/components/searchForm__c/style.scss +2 -1
- package/template/neo-web-form/package.json +1 -1
- package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +179 -59
- package/template/neo-web-form/src/components/batchAddTable__c/model.ts +12 -14
- package/template/neo-web-form/src/components/listSummary__c/index.tsx +6 -5
- package/template/react-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-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/queryObjectData.ts +0 -112
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
3
|
-
* @description
|
|
2
|
+
* @file Stage Switch Card Component
|
|
3
|
+
* @description Toggle between different sales stage data; opportunities from xObject.query, stages from stage API
|
|
4
4
|
*/
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { Spin } from 'antd';
|
|
@@ -33,7 +33,7 @@ interface StageTab {
|
|
|
33
33
|
name: string;
|
|
34
34
|
amount: string;
|
|
35
35
|
count: number;
|
|
36
|
-
/**
|
|
36
|
+
/** Can be empty object when no period-over-period data, not rendered */
|
|
37
37
|
changes?: Record<string, unknown>;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -51,11 +51,11 @@ interface StageSwitchProps {
|
|
|
51
51
|
interface StageSwitchState {
|
|
52
52
|
activeStage: string;
|
|
53
53
|
opportunityList: Record<string, unknown>[];
|
|
54
|
-
/**
|
|
54
|
+
/** Display stages generated from API + getOpportunityList */
|
|
55
55
|
stages: StageTab[];
|
|
56
56
|
loading: boolean;
|
|
57
57
|
error: string | null;
|
|
58
|
-
/**
|
|
58
|
+
/** Consistent with FilterBar event payload (closeDateCustomRange / opportunityOwner / businessType, etc.); falls back to defaultFilter when empty object */
|
|
59
59
|
filter: any;
|
|
60
60
|
defaultEntityTypeApiKey: string;
|
|
61
61
|
}
|
|
@@ -93,15 +93,15 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
93
93
|
this.fetchAllData();
|
|
94
94
|
|
|
95
95
|
/*
|
|
96
|
-
//
|
|
96
|
+
// Listen to a broadcast event
|
|
97
97
|
NeoEvent.listen('updateFilterData', (filterData: any) => {
|
|
98
|
-
console.log('StageSwitch
|
|
98
|
+
console.log('StageSwitch received a broadcast event updateFilterData: ', filterData);
|
|
99
99
|
this.setFilter(filterData);
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
//
|
|
102
|
+
// Broadcast event: update the currently active sales stage
|
|
103
103
|
NeoEvent.listen('updateActiveStage', (activeStage: string) => {
|
|
104
|
-
console.log('SimpleTable
|
|
104
|
+
console.log('SimpleTable received a broadcast event updateActiveStage: ', activeStage);
|
|
105
105
|
this.updateActiveStage(activeStage);
|
|
106
106
|
});
|
|
107
107
|
*/
|
|
@@ -117,8 +117,9 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
120
|
+
/** Convert FilterBar structured filter to xObject.query where string array (SDK concatenates with AND in order) */
|
|
121
121
|
getOpportunityQueryFilter(): string[] {
|
|
122
|
+
const defaultFilter = getDefaultFilterByProps(this.props);
|
|
122
123
|
const raw = this.state.filter;
|
|
123
124
|
const curFilter: Record<string, unknown> =
|
|
124
125
|
raw && typeof raw === 'object' && !Array.isArray(raw)
|
|
@@ -149,8 +150,9 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
149
150
|
return where;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
/**
|
|
153
|
+
/** Generate query conditions for fetching historical opportunity list data */
|
|
153
154
|
getHistoryOpportunityQueryFilter(): string[] {
|
|
155
|
+
const defaultFilter = getDefaultFilterByProps(this.props);
|
|
154
156
|
const raw = this.state.filter;
|
|
155
157
|
const curFilter: Record<string, unknown> =
|
|
156
158
|
raw && typeof raw === 'object' && !Array.isArray(raw)
|
|
@@ -187,7 +189,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
187
189
|
return where;
|
|
188
190
|
}
|
|
189
191
|
|
|
190
|
-
/**
|
|
192
|
+
/** Query up to 1000 opportunities per request */
|
|
191
193
|
async fetchOpportunityList(): Promise<Record<string, unknown>[]> {
|
|
192
194
|
const result = await xObject.query({
|
|
193
195
|
xObjectApiKey: 'opportunity',
|
|
@@ -202,7 +204,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
202
204
|
: [];
|
|
203
205
|
}
|
|
204
206
|
|
|
205
|
-
/**
|
|
207
|
+
/** Query historical opportunity list data */
|
|
206
208
|
async fetchHistoryOpportunityList(): Promise<unknown[]> {
|
|
207
209
|
const result = await queryByCustomSQL({
|
|
208
210
|
xObjectApiKey: 'biCustomModel_397169_20260401104916618',
|
|
@@ -220,16 +222,16 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
/**
|
|
223
|
-
*
|
|
224
|
-
* @returns count —
|
|
225
|
+
* Filter opportunities by stageName: extract the segment after the first `.` in stageName as the stage key, and compare with the stage text on the opportunity row using the same rule.
|
|
226
|
+
* @returns count — number of records; amount — sum of money (or amount field if money is absent)
|
|
225
227
|
*/
|
|
226
228
|
getOpportunityStats(
|
|
227
229
|
stageName: string,
|
|
228
|
-
list: Record<string, unknown>[], //
|
|
230
|
+
list: Record<string, unknown>[], // opportunity list
|
|
229
231
|
): { amount: number; count: number } {
|
|
230
232
|
const key = extractStageKeyFromStageName(stageName);
|
|
231
|
-
let amount = 0; //
|
|
232
|
-
let count = 0; //
|
|
233
|
+
let amount = 0; // total opportunity amount
|
|
234
|
+
let count = 0; // total opportunity count
|
|
233
235
|
|
|
234
236
|
for (const row of list) {
|
|
235
237
|
if (!row || typeof row !== 'object') continue;
|
|
@@ -244,9 +246,9 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
/**
|
|
247
|
-
*
|
|
248
|
-
* `[opportunityName, saleStageId, money, ...]` —
|
|
249
|
-
* @returns count —
|
|
249
|
+
* Historical opportunity list is a 2D array returned by custom SQL:
|
|
250
|
+
* `[opportunityName, saleStageId, money, ...]` — column 2 is the stage ID (corresponds to stage `key`), column 3 is the sales amount.
|
|
251
|
+
* @returns count — number of records; amount — sum of sales amount
|
|
250
252
|
*/
|
|
251
253
|
getHistoryOpportunityStats(
|
|
252
254
|
stageKey: string,
|
|
@@ -284,9 +286,9 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
buildStagesFromApi(
|
|
287
|
-
rows: any[], //
|
|
288
|
-
opportunityList: Record<string, unknown>[], //
|
|
289
|
-
historyOpportunityList: unknown[], //
|
|
289
|
+
rows: any[], // stage list
|
|
290
|
+
opportunityList: Record<string, unknown>[], // opportunity list
|
|
291
|
+
historyOpportunityList: unknown[], // historical opportunities: SQL 2D array rows
|
|
290
292
|
): StageTab[] {
|
|
291
293
|
console.log(
|
|
292
294
|
'[StageSwitch__c] buildStagesFromApi:',
|
|
@@ -304,7 +306,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
304
306
|
row.id ?? row.apiKey ?? (stageName || `stage-${index}`),
|
|
305
307
|
);
|
|
306
308
|
|
|
307
|
-
//
|
|
309
|
+
// Find the corresponding opportunity from the historical opportunity list by opportunity ID
|
|
308
310
|
const { amount: historyAmount, count: historyCount } =
|
|
309
311
|
this.getHistoryOpportunityStats(key, historyOpportunityList);
|
|
310
312
|
|
|
@@ -357,8 +359,8 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
357
359
|
loading: false,
|
|
358
360
|
});
|
|
359
361
|
} catch (e: unknown) {
|
|
360
|
-
console.error('StageSwitch
|
|
361
|
-
const msg = e instanceof Error ? e.message : '
|
|
362
|
+
console.error('StageSwitch failed to load:', e);
|
|
363
|
+
const msg = e instanceof Error ? e.message : 'Failed to load';
|
|
362
364
|
this.setState({
|
|
363
365
|
loading: false,
|
|
364
366
|
error: msg,
|
|
@@ -368,13 +370,13 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
368
370
|
}
|
|
369
371
|
}
|
|
370
372
|
|
|
371
|
-
//
|
|
373
|
+
// When a stage is clicked, update the currently active sales stage
|
|
372
374
|
handleStageClick(stage: StageTab) {
|
|
373
375
|
const { onStageChange } = this.props;
|
|
374
376
|
if (onStageChange) {
|
|
375
377
|
onStageChange(stage);
|
|
376
378
|
}
|
|
377
|
-
//
|
|
379
|
+
// Broadcast event: update the currently active sales stage
|
|
378
380
|
const activeStage = extractStageKeyFromStageName(stage.name);
|
|
379
381
|
this.setState({ activeStage: activeStage });
|
|
380
382
|
|
|
@@ -382,7 +384,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
382
384
|
activeStage,
|
|
383
385
|
});
|
|
384
386
|
|
|
385
|
-
//
|
|
387
|
+
// Trigger a broadcast event
|
|
386
388
|
// NeoEvent.broadcast('updateActiveStage', activeStage);
|
|
387
389
|
}
|
|
388
390
|
|
|
@@ -392,7 +394,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
392
394
|
}
|
|
393
395
|
|
|
394
396
|
/**
|
|
395
|
-
*
|
|
397
|
+
* Consistent with pipelineFunnel: pass in { relation, filter }, will re-fetch opportunities and merge stage statistics.
|
|
396
398
|
*/
|
|
397
399
|
@NeoEvent.function
|
|
398
400
|
setFilter(filter?: any) {
|
|
@@ -413,7 +415,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
413
415
|
}
|
|
414
416
|
|
|
415
417
|
/**
|
|
416
|
-
*
|
|
418
|
+
* Update the currently active sales stage
|
|
417
419
|
*/
|
|
418
420
|
@NeoEvent.function
|
|
419
421
|
updateActiveStage(activeStage?: string) {
|
|
@@ -435,7 +437,7 @@ class StageSwitch extends BaseCmp<StageSwitchProps, StageSwitchState> {
|
|
|
435
437
|
<div
|
|
436
438
|
className={`stageSwitch__c ${className || ''}`}
|
|
437
439
|
style={style}
|
|
438
|
-
data-time="2026.4.
|
|
440
|
+
data-time="2026.4.17 01"
|
|
439
441
|
>
|
|
440
442
|
<Spin spinning={loading}>
|
|
441
443
|
{error ? (
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export class StageSwitchModel {
|
|
2
|
-
label: string = '
|
|
3
|
-
description: string =
|
|
2
|
+
label: string = 'Stage Switch Card';
|
|
3
|
+
description: string =
|
|
4
|
+
'Toggle between different sales stage data, including amount, count and change trends';
|
|
4
5
|
iconUrl: string = 'https://custom-widgets.bj.bcebos.com/card.svg';
|
|
5
6
|
targetPage: string[] = ['all'];
|
|
6
7
|
targetDevice: string = 'all';
|
|
7
8
|
|
|
8
9
|
defaultComProps = {
|
|
9
|
-
/**
|
|
10
|
+
/** defaultEntityTypeApiKey passed when requesting the stage list API */
|
|
10
11
|
// defaultEntityTypeApiKey: 'defaultBusiType',
|
|
11
12
|
stages: [] as unknown[],
|
|
12
13
|
activeStage: 'Prospecting',
|
|
@@ -15,23 +16,35 @@ export class StageSwitchModel {
|
|
|
15
16
|
events = [
|
|
16
17
|
{
|
|
17
18
|
apiKey: 'onActiveStageChange',
|
|
18
|
-
label: '
|
|
19
|
+
label: 'After active stage changes',
|
|
19
20
|
helpText:
|
|
20
|
-
'
|
|
21
|
-
eventParams:
|
|
22
|
-
|
|
21
|
+
'Triggered after the active stage changes; event params include activeStage (current active stage)',
|
|
22
|
+
eventParams: [
|
|
23
|
+
{
|
|
24
|
+
apiKey: 'eventParam',
|
|
25
|
+
children: [
|
|
26
|
+
{
|
|
27
|
+
apiKey: 'activeStage',
|
|
28
|
+
label: 'Current active stage',
|
|
29
|
+
type: 'String',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
label: 'Event parameters',
|
|
33
|
+
type: 'Object',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
23
36
|
},
|
|
24
37
|
];
|
|
25
38
|
|
|
26
39
|
functions = [
|
|
27
40
|
{
|
|
28
41
|
apiKey: 'updateActiveStage',
|
|
29
|
-
label: '
|
|
30
|
-
helpTextKey: '
|
|
42
|
+
label: 'Update active stage',
|
|
43
|
+
helpTextKey: 'Set the current active sales stage',
|
|
31
44
|
funcInParams: [
|
|
32
45
|
{
|
|
33
46
|
apiKey: 'activeStage',
|
|
34
|
-
label: '
|
|
47
|
+
label: 'Current active stage',
|
|
35
48
|
type: 'String',
|
|
36
49
|
required: false,
|
|
37
50
|
},
|
|
@@ -39,13 +52,13 @@ export class StageSwitchModel {
|
|
|
39
52
|
},
|
|
40
53
|
{
|
|
41
54
|
apiKey: 'setFilter',
|
|
42
|
-
label: '
|
|
55
|
+
label: 'Set filter conditions',
|
|
43
56
|
helpTextKey:
|
|
44
|
-
'
|
|
57
|
+
'Set the filter consistent with the chart (closeDate / ownerId / entityType) and re-fetch opportunities',
|
|
45
58
|
funcInParams: [
|
|
46
59
|
{
|
|
47
60
|
apiKey: 'filter',
|
|
48
|
-
label: '
|
|
61
|
+
label: 'Filter conditions (relation + filter array)',
|
|
49
62
|
type: 'Object',
|
|
50
63
|
required: false,
|
|
51
64
|
},
|
|
@@ -53,8 +66,8 @@ export class StageSwitchModel {
|
|
|
53
66
|
},
|
|
54
67
|
{
|
|
55
68
|
apiKey: 'refreshData',
|
|
56
|
-
label: '
|
|
57
|
-
helpTextKey: '
|
|
69
|
+
label: 'Refresh data',
|
|
70
|
+
helpTextKey: 'Re-fetch opportunity list and stage list',
|
|
58
71
|
},
|
|
59
72
|
];
|
|
60
73
|
|
|
@@ -62,7 +75,7 @@ export class StageSwitchModel {
|
|
|
62
75
|
{
|
|
63
76
|
type: 'panelInput',
|
|
64
77
|
name: 'defaultEntityTypeApiKey',
|
|
65
|
-
label: '
|
|
78
|
+
label: 'Business type ApiKey (stage list API)',
|
|
66
79
|
},
|
|
67
80
|
];
|
|
68
81
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# StageTimeChart
|
|
1
|
+
# StageTimeChart Component
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stage time component that displays the average time spent in each sales stage.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import StageTimeChart from './components/stageTimeChart__c';
|
|
@@ -10,7 +10,7 @@ import StageTimeChart from './components/stageTimeChart__c';
|
|
|
10
10
|
<StageTimeChart
|
|
11
11
|
title="Avg. Time in Stage"
|
|
12
12
|
items={[
|
|
13
|
-
{ stageName: 'Prospecting', actualTime: '
|
|
13
|
+
{ stageName: 'Prospecting', actualTime: '8d 6h', actualPercent: 28, actualColor: '#22c55e', targetPercent: 33, limitPercent: 50 },
|
|
14
14
|
]}
|
|
15
15
|
onStageClick={(name) => console.log('Clicked:', name)}
|
|
16
16
|
/>
|
|
@@ -18,20 +18,20 @@ import StageTimeChart from './components/stageTimeChart__c';
|
|
|
18
18
|
|
|
19
19
|
## Props
|
|
20
20
|
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| title |
|
|
24
|
-
| items |
|
|
25
|
-
| showAiButton |
|
|
26
|
-
| onStageClick |
|
|
21
|
+
| Property | Description | Type | Default |
|
|
22
|
+
|----------|-------------|------|---------|
|
|
23
|
+
| title | Title | string | 'Avg. Time in Stage' |
|
|
24
|
+
| items | Stage data | StageTimeItem[] | [] |
|
|
25
|
+
| showAiButton | Show AI button | boolean | true |
|
|
26
|
+
| onStageClick | Stage click callback | (stageName: string) => void | - |
|
|
27
27
|
|
|
28
28
|
## StageTimeItem
|
|
29
29
|
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
| stageName |
|
|
33
|
-
| actualTime |
|
|
34
|
-
| actualPercent |
|
|
35
|
-
| actualColor |
|
|
36
|
-
| targetPercent |
|
|
37
|
-
| limitPercent |
|
|
30
|
+
| Property | Description | Type |
|
|
31
|
+
|----------|-------------|------|
|
|
32
|
+
| stageName | Stage name | string |
|
|
33
|
+
| actualTime | Actual time | string |
|
|
34
|
+
| actualPercent | Actual percentage | number |
|
|
35
|
+
| actualColor | Actual color | string |
|
|
36
|
+
| targetPercent | Target percentage | number |
|
|
37
|
+
| limitPercent | Limit percentage | number |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
3
|
-
* @description
|
|
2
|
+
* @file Stage Time Component
|
|
3
|
+
* @description Displays the average time spent in each sales stage; chart data from NeoBI queryDataTask, target/limit from XObject customItem3/4 (days)
|
|
4
4
|
*/
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { Spin } from 'antd';
|
|
@@ -52,7 +52,7 @@ interface StageTimeChartProps {
|
|
|
52
52
|
style?: React.CSSProperties;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/**
|
|
55
|
+
/** Threshold row: only normalize customItem3/4 to number, keep other fields as-is */
|
|
56
56
|
interface ThresholdRecordRow extends Record<string, unknown> {
|
|
57
57
|
customItem3__c: number;
|
|
58
58
|
customItem4__c: number;
|
|
@@ -63,14 +63,14 @@ interface StageTimeChartState {
|
|
|
63
63
|
error: string | null;
|
|
64
64
|
items: StageTimeItem[];
|
|
65
65
|
filter: any;
|
|
66
|
-
/**
|
|
66
|
+
/** Matches threshold row customItem2__c, default New Business (can be updated by setFilter) */
|
|
67
67
|
oppType: string;
|
|
68
|
-
/** fetchThresholdRecords
|
|
68
|
+
/** Full list fetched by fetchThresholdRecords (customItem3/4 converted to number) */
|
|
69
69
|
thresholdRecords: ThresholdRecordRow[];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
73
|
-
/**
|
|
73
|
+
/** Initialize state */
|
|
74
74
|
constructor(props: StageTimeChartProps) {
|
|
75
75
|
super(props);
|
|
76
76
|
const initialOppType =
|
|
@@ -94,20 +94,20 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
94
94
|
this.setFilter = this.setFilter.bind(this);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/**
|
|
97
|
+
/** Fetch thresholds and chart data after mount */
|
|
98
98
|
componentDidMount() {
|
|
99
99
|
this.fetchAllData();
|
|
100
100
|
|
|
101
101
|
/*
|
|
102
|
-
//
|
|
102
|
+
// Listen to a broadcast event
|
|
103
103
|
NeoEvent.listen('updateFilterData', (filterData: any) => {
|
|
104
|
-
console.log('StageTimeChart
|
|
104
|
+
console.log('StageTimeChart received broadcast event updateFilterData: ', filterData);
|
|
105
105
|
this.setFilter(filterData);
|
|
106
106
|
});
|
|
107
107
|
*/
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
/**
|
|
110
|
+
/** Re-fetch data when key props change */
|
|
111
111
|
componentDidUpdate(
|
|
112
112
|
prevProps: StageTimeChartProps,
|
|
113
113
|
prevState: StageTimeChartState,
|
|
@@ -134,7 +134,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
/**
|
|
137
|
+
/** Find a single row in the list by customItem1__c / customItem2__c; customItem2 defaults to state.oppType */
|
|
138
138
|
getThresholdRecord(
|
|
139
139
|
customItem1__c: string = String(this.state.oppType ?? 'New Business'),
|
|
140
140
|
customItem2__c: string,
|
|
@@ -150,7 +150,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/**
|
|
153
|
+
/** Fetch threshold list, only convert customItem3/4 to number, and write to state.thresholdRecords */
|
|
154
154
|
async fetchThresholdRecords(): Promise<ThresholdRecordRow[]> {
|
|
155
155
|
const { thresholdsXObjectApi } = this.props;
|
|
156
156
|
const apiKey = thresholdsXObjectApi?.xObjectApiKey;
|
|
@@ -180,7 +180,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
180
180
|
|
|
181
181
|
if (!result?.status) {
|
|
182
182
|
console.warn(
|
|
183
|
-
'StageTimeChart xObject.query(thresholds)
|
|
183
|
+
'StageTimeChart xObject.query(thresholds) not successful:',
|
|
184
184
|
result?.msg ?? result,
|
|
185
185
|
);
|
|
186
186
|
this.setState({ thresholdRecords: [] });
|
|
@@ -199,13 +199,13 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
199
199
|
this.setState({ thresholdRecords });
|
|
200
200
|
return thresholdRecords;
|
|
201
201
|
} catch (e) {
|
|
202
|
-
console.error('StageTimeChart
|
|
202
|
+
console.error('StageTimeChart failed to load threshold data:', e);
|
|
203
203
|
this.setState({ thresholdRecords: [] });
|
|
204
204
|
return [];
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
/**
|
|
208
|
+
/** Request queryDataTask, parse table rows and attach target/limit for each row (from threshold row customItem3/4) */
|
|
209
209
|
async fetchChartRows(
|
|
210
210
|
thresholdRecords: ThresholdRecordRow[],
|
|
211
211
|
): Promise<{ rows: StageTimeRow[]; error: string | null }> {
|
|
@@ -216,7 +216,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
216
216
|
if (viewId == null || viewId === '' || userId == null || userId === '') {
|
|
217
217
|
return {
|
|
218
218
|
rows: [],
|
|
219
|
-
error: '
|
|
219
|
+
error: 'Missing viewId or current user id (data.__NeoCurrentUser.id)',
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -248,7 +248,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
248
248
|
if (status !== 0 || !Array.isArray(table)) {
|
|
249
249
|
return {
|
|
250
250
|
rows: [],
|
|
251
|
-
error: res?.message || res?.msg || '
|
|
251
|
+
error: res?.message || res?.msg || 'Failed to query chart data',
|
|
252
252
|
};
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -286,10 +286,10 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
286
286
|
|
|
287
287
|
return { rows, error: null };
|
|
288
288
|
} catch (e: any) {
|
|
289
|
-
console.error('StageTimeChart queryDataTask
|
|
289
|
+
console.error('StageTimeChart queryDataTask failed:', e);
|
|
290
290
|
return {
|
|
291
291
|
rows: [],
|
|
292
|
-
error: e?.message || '
|
|
292
|
+
error: e?.message || 'Network request failed',
|
|
293
293
|
};
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -370,7 +370,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
370
370
|
<div
|
|
371
371
|
className={`stageTimeChart__c ${className || ''}`}
|
|
372
372
|
style={style}
|
|
373
|
-
data-time="2026.4.
|
|
373
|
+
data-time="2026.4.17 01"
|
|
374
374
|
>
|
|
375
375
|
<Spin spinning={loading}>
|
|
376
376
|
<div className="chart-header">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class StageTimeChartModel {
|
|
2
|
-
label: string = '
|
|
2
|
+
label: string = 'Stage Time';
|
|
3
3
|
description: string =
|
|
4
|
-
'
|
|
4
|
+
'Displays the average time spent in each sales stage; chart data from NeoBI queryDataTask, target/limit from XObject customItem3/4 (days)';
|
|
5
5
|
iconUrl: string = 'https://custom-widgets.bj.bcebos.com/barChart.svg';
|
|
6
6
|
targetPage: string[] = ['all'];
|
|
7
7
|
targetDevice: string = 'all';
|
|
@@ -11,9 +11,9 @@ export class StageTimeChartModel {
|
|
|
11
11
|
viewId: '4264466340118875',
|
|
12
12
|
viewType: 'sync',
|
|
13
13
|
showAiButton: true,
|
|
14
|
-
/**
|
|
14
|
+
/** Opportunity type matching threshold record customItem2__c, default New Business */
|
|
15
15
|
oppType: 'New Business',
|
|
16
|
-
/**
|
|
16
|
+
/** Thresholds: customItem1__c stage key, customItem2__c opportunity type, customItem3__c/4 target and limit (days) */
|
|
17
17
|
thresholdsXObjectApi: {
|
|
18
18
|
xObjectApiKey: 'customEntity105__c',
|
|
19
19
|
fields: [
|
|
@@ -30,28 +30,41 @@ export class StageTimeChartModel {
|
|
|
30
30
|
events = [
|
|
31
31
|
{
|
|
32
32
|
apiKey: 'onActiveStageChange',
|
|
33
|
-
label: '
|
|
33
|
+
label: 'Triggered on stage bar click',
|
|
34
34
|
helpText:
|
|
35
|
-
'
|
|
36
|
-
eventParams:
|
|
37
|
-
|
|
35
|
+
'Triggered when clicking a stage bar; event params include activeStage (current active stage)',
|
|
36
|
+
eventParams: [
|
|
37
|
+
{
|
|
38
|
+
apiKey: 'eventParam',
|
|
39
|
+
children: [
|
|
40
|
+
{
|
|
41
|
+
apiKey: 'activeStage',
|
|
42
|
+
label: 'Current active stage',
|
|
43
|
+
type: 'String',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
label: 'Event parameters',
|
|
47
|
+
type: 'Object',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
38
50
|
},
|
|
39
51
|
];
|
|
40
52
|
|
|
41
53
|
functions = [
|
|
42
54
|
{
|
|
43
55
|
apiKey: 'refreshData',
|
|
44
|
-
label: '
|
|
45
|
-
helpTextKey:
|
|
56
|
+
label: 'Refresh data',
|
|
57
|
+
helpTextKey:
|
|
58
|
+
'Re-request queryDataTask and threshold config to refresh stage time',
|
|
46
59
|
},
|
|
47
60
|
{
|
|
48
61
|
apiKey: 'setFilter',
|
|
49
|
-
label: '
|
|
50
|
-
helpTextKey: '
|
|
62
|
+
label: 'Set filter conditions',
|
|
63
|
+
helpTextKey: 'Set the queryDataTask filter and re-fetch chart data',
|
|
51
64
|
funcInParams: [
|
|
52
65
|
{
|
|
53
66
|
apiKey: 'filter',
|
|
54
|
-
label: '
|
|
67
|
+
label: 'Filter conditions',
|
|
55
68
|
type: 'Object',
|
|
56
69
|
required: false,
|
|
57
70
|
},
|
|
@@ -63,27 +76,28 @@ export class StageTimeChartModel {
|
|
|
63
76
|
{
|
|
64
77
|
type: 'panelInput',
|
|
65
78
|
name: 'title',
|
|
66
|
-
label: '
|
|
79
|
+
label: 'Title',
|
|
67
80
|
},
|
|
68
81
|
{
|
|
69
82
|
type: 'panelInput',
|
|
70
83
|
name: 'viewId',
|
|
71
|
-
label: '
|
|
84
|
+
label: 'View ID',
|
|
72
85
|
},
|
|
73
86
|
{
|
|
74
87
|
type: 'panelInput',
|
|
75
88
|
name: 'viewType',
|
|
76
|
-
label: '
|
|
89
|
+
label: 'View type (request type)',
|
|
77
90
|
},
|
|
78
91
|
{
|
|
79
92
|
type: 'panelInput',
|
|
80
93
|
name: 'oppType',
|
|
81
|
-
label: '
|
|
94
|
+
label: 'Opportunity type (matching customItem2__c)',
|
|
82
95
|
},
|
|
96
|
+
/*
|
|
83
97
|
{
|
|
84
98
|
type: 'xObjectDataApi',
|
|
85
99
|
name: 'thresholdsXObjectApi',
|
|
86
|
-
label: '
|
|
100
|
+
label: 'Stage target/limit data source',
|
|
87
101
|
value: {
|
|
88
102
|
xObjectApiKey: '',
|
|
89
103
|
fields: [
|
|
@@ -94,9 +108,10 @@ export class StageTimeChartModel {
|
|
|
94
108
|
],
|
|
95
109
|
},
|
|
96
110
|
placeholder:
|
|
97
|
-
'
|
|
111
|
+
'Entity must contain customItem1__c (stage), customItem2__c (opportunity type), customItem3/4 (target/limit days)',
|
|
98
112
|
custom: true,
|
|
99
113
|
},
|
|
114
|
+
*/
|
|
100
115
|
];
|
|
101
116
|
}
|
|
102
117
|
|