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