neo-cmp-cli 1.13.17 → 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/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/targetNumber__c/model.ts +1 -1
- 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 +13 -11
- 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 +23 -22
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +18 -17
- 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 +16 -15
- 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 +21 -18
- 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-form/package.json +1 -1
- 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/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
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { request as axiosFetcher } from 'neo-open-api';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Common query Open APIs are stored here
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/** Normalize where to SQL fragment: string is used directly; array items are joined with 'and' */
|
|
9
9
|
function normalizeWhere(where: unknown): string {
|
|
10
10
|
if (where == null || where === '') {
|
|
11
11
|
return '';
|
|
@@ -22,7 +22,7 @@ function normalizeWhere(where: unknown): string {
|
|
|
22
22
|
return '';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Query BI data by SQL
|
|
26
26
|
export const queryByCustomSQL = async (options?: any) => {
|
|
27
27
|
const apiUrl = '/rest/neobi/v2.0/query/queryByCustomSQL';
|
|
28
28
|
const curOptions = options || {};
|
|
@@ -31,34 +31,34 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
31
31
|
const page = curOptions.page || 1;
|
|
32
32
|
const pageSize = curOptions.pageSize || 10;
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Automatically add objectId field
|
|
35
35
|
if (!fields.includes('id')) {
|
|
36
36
|
fields.push('id');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// Calculate pagination offset
|
|
40
40
|
const offset = (page - 1) * pageSize;
|
|
41
41
|
|
|
42
|
-
//
|
|
42
|
+
// Build SQL query
|
|
43
43
|
let querySql = `select ${fields.join(',')} from ${xObjectApiKey}`;
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// Add sort conditions (if any)
|
|
46
46
|
if (curOptions.orderBy) {
|
|
47
47
|
querySql += ` order by ${curOptions.orderBy}`;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* "like"
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
51
|
+
* Add filter conditions (if any)
|
|
52
|
+
* Supported operators: =, !=, like, not like, not in, is not null, is null, >, <, <>, >=, <=, in, between ... and ...
|
|
53
|
+
* Notes for =, like and in:
|
|
54
|
+
* "=" as a string condition means exact match. For example, city = 'Beijing' returns all records where city is strictly equal to "Beijing".
|
|
55
|
+
* "like" as a string condition requires "%" wildcard for fuzzy matching. For example, city like 'Beijing%' returns all records where city starts with "Beijing".
|
|
56
|
+
* Currently only supports placing "%" after the known content; e.g., city like '% Beijing' is not supported.
|
|
57
|
+
* When the SQL query contains special characters like "%", URL encoding is required.
|
|
58
|
+
* Supports "in", but not subqueries.
|
|
59
|
+
* Supported logical operators: and, or.
|
|
60
60
|
*
|
|
61
|
-
* `where`
|
|
61
|
+
* `where` can be a string, or a string array (multiple items joined with 'and' by default, equivalent to `a and b`).
|
|
62
62
|
*/
|
|
63
63
|
const whereClause = normalizeWhere(curOptions.where);
|
|
64
64
|
|
|
@@ -67,7 +67,7 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (curOptions.page || curOptions.pageSize) {
|
|
70
|
-
//
|
|
70
|
+
// Add pagination limit
|
|
71
71
|
querySql += ` limit ${offset},${pageSize}`;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -91,7 +91,8 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
91
91
|
return {
|
|
92
92
|
status: true,
|
|
93
93
|
code: resultData.code,
|
|
94
|
-
msg:
|
|
94
|
+
msg:
|
|
95
|
+
resultData.msg || 'Successfully retrieved business object data list',
|
|
95
96
|
totalSize: records.length,
|
|
96
97
|
data: records || [],
|
|
97
98
|
};
|
|
@@ -100,15 +101,18 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
100
101
|
return {
|
|
101
102
|
status: false,
|
|
102
103
|
code: resultData.code,
|
|
103
|
-
msg: resultData.msg || '
|
|
104
|
+
msg: resultData.msg || 'Failed to retrieve business object data list',
|
|
104
105
|
data: [],
|
|
105
106
|
};
|
|
106
107
|
} catch (error) {
|
|
107
|
-
console.error('
|
|
108
|
+
console.error('Failed to retrieve business object data list:', error);
|
|
108
109
|
|
|
109
110
|
return {
|
|
110
111
|
status: false,
|
|
111
|
-
msg:
|
|
112
|
+
msg:
|
|
113
|
+
error.msg ||
|
|
114
|
+
error.message ||
|
|
115
|
+
'Failed to retrieve business object data list',
|
|
112
116
|
data: [],
|
|
113
117
|
};
|
|
114
118
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import debounce from 'lodash/debounce';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** Data request debounce interval in milliseconds for components, used to merge repeated triggers within a short time */
|
|
5
5
|
export const REQUEST_DEBOUNCE_MS = 280;
|
|
6
6
|
|
|
7
|
-
/** lodash debounce
|
|
7
|
+
/** lodash debounce instance (avoids dependency on @types/lodash) */
|
|
8
8
|
export type DebouncedRequestFn = {
|
|
9
9
|
(): void;
|
|
10
10
|
cancel(): void;
|
|
@@ -12,7 +12,7 @@ export type DebouncedRequestFn = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Trailing debounce: executes only once after a pause during continuous triggers, suitable for filter / props rapid update scenarios.
|
|
16
16
|
*/
|
|
17
17
|
export function createRequestDebounce(
|
|
18
18
|
fn: () => void,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Table simpleTable specific: formatting, history snapshots, trends and cell rendering
|
|
3
3
|
*/
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { Card, Popover } from 'antd';
|
|
@@ -7,7 +7,7 @@ import moment from 'moment';
|
|
|
7
7
|
|
|
8
8
|
import { toFiniteNumber } from './common';
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/** Period-start opportunity snapshot (associated with current row id) */
|
|
11
11
|
export interface HistoryOppSnap {
|
|
12
12
|
id: string;
|
|
13
13
|
closeDate: unknown;
|
|
@@ -67,7 +67,7 @@ function closeDateTrend(
|
|
|
67
67
|
return 'same';
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
70
|
+
/** Custom SQL return row: array column order matches HISTORY_QUERY_FIELDS; or object fields */
|
|
71
71
|
export function historyRowToSnap(row: unknown): HistoryOppSnap | null {
|
|
72
72
|
if (row == null) return null;
|
|
73
73
|
if (Array.isArray(row)) {
|
|
@@ -91,9 +91,7 @@ export function historyRowToSnap(row: unknown): HistoryOppSnap | null {
|
|
|
91
91
|
opportunityName: String(
|
|
92
92
|
o.opportunity_1_opportunityName ?? o.opportunityName ?? '',
|
|
93
93
|
),
|
|
94
|
-
saleStageId: String(
|
|
95
|
-
o.opportunity_1_saleStageId ?? o.saleStageId ?? '',
|
|
96
|
-
),
|
|
94
|
+
saleStageId: String(o.opportunity_1_saleStageId ?? o.saleStageId ?? ''),
|
|
97
95
|
money: o.opportunity_1_money ?? o.money,
|
|
98
96
|
};
|
|
99
97
|
}
|
|
@@ -123,7 +121,7 @@ export function formatActivityDateTime(value: unknown): string {
|
|
|
123
121
|
return m.isValid() ? m.format('YYYY-MM-DD HH:mm:ss') : String(value);
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
/** customItem246/247
|
|
124
|
+
/** customItem246/247: change record time (ms/s timestamp or moment-parseable value) -> YYYY-MM-DD */
|
|
127
125
|
export function formatChangeRecordedAt(value: unknown): string {
|
|
128
126
|
if (value == null || value === '') return '—';
|
|
129
127
|
const n = toFiniteNumber(value);
|
|
@@ -136,7 +134,7 @@ export function formatChangeRecordedAt(value: unknown): string {
|
|
|
136
134
|
return m.isValid() ? m.format('YYYY-MM-DD') : String(value);
|
|
137
135
|
}
|
|
138
136
|
|
|
139
|
-
/**
|
|
137
|
+
/** Money column: $ prefix + thousands separator (no abbreviation) */
|
|
140
138
|
export function formatMoneyCell(value: unknown): string {
|
|
141
139
|
if (value == null || value === '') return '—';
|
|
142
140
|
const n = parseFloat(String(value).replace(/,/g, '').trim());
|
|
@@ -150,8 +148,10 @@ export function formatMoneyCell(value: unknown): string {
|
|
|
150
148
|
return `$${part}`;
|
|
151
149
|
}
|
|
152
150
|
|
|
153
|
-
/** oppHealthAssessmentLevel
|
|
154
|
-
export function renderOppHealthAssessmentLevel(
|
|
151
|
+
/** oppHealthAssessmentLevel: numeric value -> health label (with colored background) */
|
|
152
|
+
export function renderOppHealthAssessmentLevel(
|
|
153
|
+
value: unknown,
|
|
154
|
+
): React.ReactNode {
|
|
155
155
|
if (value == null || value === '') return '—';
|
|
156
156
|
const n = toFiniteNumber(value);
|
|
157
157
|
if (n === 5) {
|
|
@@ -160,7 +160,9 @@ export function renderOppHealthAssessmentLevel(value: unknown): React.ReactNode
|
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
if (n === 6) {
|
|
163
|
-
return
|
|
163
|
+
return (
|
|
164
|
+
<span className="opp-health-tag opp-health-tag--yellow">Health</span>
|
|
165
|
+
);
|
|
164
166
|
}
|
|
165
167
|
if (n === 7) {
|
|
166
168
|
return <span className="opp-health-tag opp-health-tag--red">Risk</span>;
|
|
@@ -172,7 +174,7 @@ export function renderOppHealthAssessmentLevel(value: unknown): React.ReactNode
|
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
/**
|
|
175
|
-
*
|
|
177
|
+
* Percentage column: appends %; API commonly returns 0-1 decimal or 0-100 numeric, both displayed in readable format
|
|
176
178
|
*/
|
|
177
179
|
export function formatPercentCell(value: unknown): string {
|
|
178
180
|
if (value == null || value === '') return '—';
|
|
@@ -205,7 +207,9 @@ export function moneyChangeTooltipTitle(
|
|
|
205
207
|
<div className="simpleTable-change-tip__detail">
|
|
206
208
|
{formatMoneyCell(h.money)}
|
|
207
209
|
{' → '}
|
|
208
|
-
<span className="simpleTable-change-tip__to">
|
|
210
|
+
<span className="simpleTable-change-tip__to">
|
|
211
|
+
{formatMoneyCell(current)}
|
|
212
|
+
</span>
|
|
209
213
|
</div>
|
|
210
214
|
<div className="simpleTable-change-tip__meta">
|
|
211
215
|
Changed on {formatChangeRecordedAt(preset)}
|
|
@@ -227,7 +231,9 @@ export function closeDateChangeTooltipTitle(
|
|
|
227
231
|
<div className="simpleTable-change-tip__detail">
|
|
228
232
|
{formatCloseDate(h.closeDate)}
|
|
229
233
|
{' → '}
|
|
230
|
-
<span className="simpleTable-change-tip__to">
|
|
234
|
+
<span className="simpleTable-change-tip__to">
|
|
235
|
+
{formatCloseDate(current)}
|
|
236
|
+
</span>
|
|
231
237
|
</div>
|
|
232
238
|
<div className="simpleTable-change-tip__meta">
|
|
233
239
|
Changed on {formatChangeRecordedAt(preset)}
|
|
@@ -268,22 +274,17 @@ export function renderTrendCell(args: {
|
|
|
268
274
|
);
|
|
269
275
|
}
|
|
270
276
|
|
|
271
|
-
/** demo.html winRateTooltip
|
|
277
|
+
/** demo.html winRateTooltip style */
|
|
272
278
|
export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
273
279
|
const baseline = record?.customItem244__c;
|
|
274
280
|
const positives = record?.customItem241__c;
|
|
275
281
|
const negatives = record?.customItem242__c;
|
|
276
282
|
const winRate = record?.customItem239__c;
|
|
277
283
|
|
|
278
|
-
const fmtLine = (v: unknown) =>
|
|
279
|
-
v == null || v === '' ? '—' : String(v);
|
|
284
|
+
const fmtLine = (v: unknown) => (v == null || v === '' ? '—' : String(v));
|
|
280
285
|
|
|
281
286
|
return (
|
|
282
|
-
<Card
|
|
283
|
-
size="small"
|
|
284
|
-
bordered={false}
|
|
285
|
-
className="simpleTable-winrate-card"
|
|
286
|
-
>
|
|
287
|
+
<Card size="small" bordered={false} className="simpleTable-winrate-card">
|
|
287
288
|
<div className="simpleTable-winrate-card__baseline">
|
|
288
289
|
Baseline Probability{' '}
|
|
289
290
|
<span className="simpleTable-winrate-card__baseline-val">
|
|
@@ -291,12 +292,16 @@ export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
|
291
292
|
</span>
|
|
292
293
|
</div>
|
|
293
294
|
<hr className="simpleTable-winrate-card__hr" />
|
|
294
|
-
<div className="simpleTable-winrate-card__section-title">
|
|
295
|
+
<div className="simpleTable-winrate-card__section-title">
|
|
296
|
+
Positive Factors
|
|
297
|
+
</div>
|
|
295
298
|
<div className="simpleTable-winrate-card__positives">
|
|
296
299
|
{fmtLine(positives)}
|
|
297
300
|
</div>
|
|
298
301
|
<hr className="simpleTable-winrate-card__hr" />
|
|
299
|
-
<div className="simpleTable-winrate-card__section-title">
|
|
302
|
+
<div className="simpleTable-winrate-card__section-title">
|
|
303
|
+
Negative Factors
|
|
304
|
+
</div>
|
|
300
305
|
<div className="simpleTable-winrate-card__negatives">
|
|
301
306
|
{fmtLine(negatives)}
|
|
302
307
|
</div>
|
|
@@ -311,7 +316,7 @@ export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
|
311
316
|
);
|
|
312
317
|
}
|
|
313
318
|
|
|
314
|
-
/**
|
|
319
|
+
/** Summary amount display (number format consistent with formatMoneyCell) */
|
|
315
320
|
export function formatMoneySummaryAmount(n: number): string {
|
|
316
321
|
const part = Number.isInteger(n)
|
|
317
322
|
? n.toLocaleString('en-US')
|
|
@@ -322,7 +327,7 @@ export function formatMoneySummaryAmount(n: number): string {
|
|
|
322
327
|
return `$${part}`;
|
|
323
328
|
}
|
|
324
329
|
|
|
325
|
-
/**
|
|
330
|
+
/** Sum row amounts by field apiKey (parsing rules consistent with money column) */
|
|
326
331
|
export function sumMoneyRows(rows: any[], moneyFieldKey: string): number {
|
|
327
332
|
let total = 0;
|
|
328
333
|
for (const row of rows) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Stage time stageTimeChart specific
|
|
3
3
|
*/
|
|
4
4
|
import { extractStageKeyFromStageName } from './common';
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ export interface StageTimeItem {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Convert duration text like "16 Days 11 Hours" or "8天6时" to days (float), e.g. 16 + 11/24
|
|
31
31
|
*/
|
|
32
32
|
export function parseDurationToDays(text: unknown): number {
|
|
33
33
|
const s = String(text ?? '').trim();
|
|
@@ -41,13 +41,13 @@ export function parseDurationToDays(text: unknown): number {
|
|
|
41
41
|
return days + hours / 24;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
44
|
+
/** Final/closed stages are excluded from "stage time" display */
|
|
45
45
|
export function isClosedOutcomeStage(stageName: string): boolean {
|
|
46
46
|
const key = extractStageKeyFromStageName(stageName).toLowerCase();
|
|
47
47
|
return key === 'closed won' || key === 'closed lost';
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
50
|
+
/** Color: compare actual days with customItem3 (target) / customItem4 (limit) */
|
|
51
51
|
export function resolveActualColor(
|
|
52
52
|
actualDays: number,
|
|
53
53
|
target: number,
|
|
@@ -58,7 +58,7 @@ export function resolveActualColor(
|
|
|
58
58
|
return COLOR_YELLOW;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/**
|
|
61
|
+
/** Convert parsed rows to chart bar data (color and bar proportions) */
|
|
62
62
|
export function buildStageTimeItems(rows: StageTimeRow[]): StageTimeItem[] {
|
|
63
63
|
return rows.map((row) => {
|
|
64
64
|
const actualPercent = row.actualTime;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Numeric indicator targetNumber: style helpers
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
/** margin/padding
|
|
5
|
+
/** margin/padding string: appends unit when no unit present, returns as-is if px/rem/etc already exists */
|
|
6
6
|
export function formatCssSpacing(value: string, unit: string): string {
|
|
7
7
|
if (!value || value === '0') return '0';
|
|
8
8
|
if (/\d+(px|rem|em|%)$/.test(value.trim())) {
|