@zestl/zvolv-widgets 0.0.1 → 0.0.2
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/CHANGELOG.md +37 -0
- package/MIRROR_NOTES.md +146 -0
- package/README.md +153 -1
- package/SKILL.md +16 -1
- package/dist/index.cjs +787 -333
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +197 -16
- package/dist/index.d.ts +197 -16
- package/dist/index.js +582 -136
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.cjs
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React14 = require('react');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
-
var
|
|
5
|
+
var dayjs4 = require('dayjs');
|
|
6
6
|
var utc = require('dayjs/plugin/utc.js');
|
|
7
7
|
var weekOfYear = require('dayjs/plugin/weekOfYear.js');
|
|
8
8
|
var _ = require('lodash');
|
|
9
|
+
var timezone2 = require('dayjs/plugin/timezone.js');
|
|
9
10
|
|
|
10
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
12
|
|
|
12
|
-
var
|
|
13
|
-
var
|
|
13
|
+
var React14__default = /*#__PURE__*/_interopDefault(React14);
|
|
14
|
+
var dayjs4__default = /*#__PURE__*/_interopDefault(dayjs4);
|
|
14
15
|
var utc__default = /*#__PURE__*/_interopDefault(utc);
|
|
15
16
|
var weekOfYear__default = /*#__PURE__*/_interopDefault(weekOfYear);
|
|
16
17
|
var ___default = /*#__PURE__*/_interopDefault(_);
|
|
18
|
+
var timezone2__default = /*#__PURE__*/_interopDefault(timezone2);
|
|
17
19
|
|
|
18
20
|
// src/core/ZvolvProvider.tsx
|
|
19
|
-
var RuntimeContext =
|
|
21
|
+
var RuntimeContext = React14.createContext(null);
|
|
20
22
|
var ZvolvProvider = ({ runtime, children }) => {
|
|
21
|
-
const value =
|
|
23
|
+
const value = React14.useMemo(() => runtime, [runtime]);
|
|
22
24
|
return /* @__PURE__ */ jsxRuntime.jsx(RuntimeContext.Provider, { value, children });
|
|
23
25
|
};
|
|
24
26
|
function useZvolvRuntime() {
|
|
25
|
-
const ctx =
|
|
27
|
+
const ctx = React14.useContext(RuntimeContext);
|
|
26
28
|
if (!ctx) {
|
|
27
29
|
throw new Error(
|
|
28
30
|
"@zestl/zvolv-widgets: no ZvolvProvider found. Wrap your app in <ZvolvProvider runtime={...}> before rendering widgets or calling data hooks."
|
|
@@ -127,8 +129,8 @@ var TASK_DATE_FIELDS = [
|
|
|
127
129
|
"completeDate"
|
|
128
130
|
];
|
|
129
131
|
new Set(TASK_DATE_FIELDS.map((f) => f.toLowerCase()));
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
dayjs4__default.default.extend(utc__default.default);
|
|
133
|
+
dayjs4__default.default.extend(weekOfYear__default.default);
|
|
132
134
|
function humanizeDurationToMinutes(value) {
|
|
133
135
|
if (!value) return 0;
|
|
134
136
|
let total = 0;
|
|
@@ -173,7 +175,7 @@ function getDataType(elementType) {
|
|
|
173
175
|
function getDateRange(ops) {
|
|
174
176
|
const values = [];
|
|
175
177
|
const fmt2 = "YYYY-MM-DD";
|
|
176
|
-
const today =
|
|
178
|
+
const today = dayjs4__default.default();
|
|
177
179
|
switch (ops) {
|
|
178
180
|
case "TODAY":
|
|
179
181
|
values[0] = today.format(fmt2);
|
|
@@ -325,7 +327,7 @@ var resolveQueryUser = () => {
|
|
|
325
327
|
};
|
|
326
328
|
|
|
327
329
|
// src/runtime/queryGenerator.ts
|
|
328
|
-
|
|
330
|
+
dayjs4__default.default.extend(utc__default.default);
|
|
329
331
|
var mappingsCache = /* @__PURE__ */ new Map();
|
|
330
332
|
function getCachedAnalyticsMappings(indexKey) {
|
|
331
333
|
let pending2 = mappingsCache.get(indexKey);
|
|
@@ -477,7 +479,7 @@ function resolveDateMath(value) {
|
|
|
477
479
|
if (typeof value !== "string") return null;
|
|
478
480
|
const match = dateMathPattern.exec(value.trim());
|
|
479
481
|
if (!match) return null;
|
|
480
|
-
let d =
|
|
482
|
+
let d = dayjs4__default.default();
|
|
481
483
|
const [, amountStr, amountUnit, , roundUnit] = match;
|
|
482
484
|
if (amountStr && amountUnit) {
|
|
483
485
|
d = d.add(parseInt(amountStr, 10), dateMathUnit[amountUnit]);
|
|
@@ -492,7 +494,7 @@ function isDateMath(value) {
|
|
|
492
494
|
}
|
|
493
495
|
function getDate(dateFormula) {
|
|
494
496
|
const fmt2 = "YYYY-MM-DDTHH:mm:ssZ";
|
|
495
|
-
const d =
|
|
497
|
+
const d = dayjs4__default.default();
|
|
496
498
|
switch (dateFormula) {
|
|
497
499
|
case "{@[TODAY]}":
|
|
498
500
|
return d.startOf("day").format(fmt2);
|
|
@@ -689,7 +691,7 @@ function getFilters(field2, type, loggedInUser = null, isTask = false) {
|
|
|
689
691
|
field2.filter.operand = field2.filter.operand.map((oprd) => {
|
|
690
692
|
if (isDateMath(oprd)) return oprd;
|
|
691
693
|
const pattern = dateFieldPattern[field2.fieldFormat] ?? field2.fieldFormat;
|
|
692
|
-
return
|
|
694
|
+
return dayjs4__default.default(oprd, pattern).format("YYYY-MM-DD");
|
|
693
695
|
});
|
|
694
696
|
}
|
|
695
697
|
if (field2["taskfield"] && !field2["masterFieldDataType"]) {
|
|
@@ -974,6 +976,142 @@ function getTaskElementSort(dataSource, fieldMetaByName) {
|
|
|
974
976
|
}
|
|
975
977
|
return null;
|
|
976
978
|
}
|
|
979
|
+
function pivotEsField(entry) {
|
|
980
|
+
return entry?.masterField || entry?.title;
|
|
981
|
+
}
|
|
982
|
+
var HISTOGRAM_INTERVALS = {
|
|
983
|
+
day: "1d",
|
|
984
|
+
week: "1w",
|
|
985
|
+
month: "1M",
|
|
986
|
+
quarter: "1q",
|
|
987
|
+
year: "1y"
|
|
988
|
+
};
|
|
989
|
+
function getDateHistogramSource(row, esField, mfType, meta, sortOrder) {
|
|
990
|
+
const isTaskDate = taskDate.includes(esField) && !!(row.isTaskField || row.taskfield || meta?.isTaskField || meta?.taskfield);
|
|
991
|
+
if (!dateTypes.includes(mfType) && !isTaskDate) {
|
|
992
|
+
console.warn(`[@zestl/zvolv-widgets] groupBy "${row.title}": interval "${row.interval}" ignored \u2014 "${esField}" is not a date field.`);
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
const calendarInterval = HISTOGRAM_INTERVALS[row.interval];
|
|
996
|
+
if (!calendarInterval) {
|
|
997
|
+
console.warn(`[@zestl/zvolv-widgets] groupBy "${row.title}": unknown interval "${row.interval}" \u2014 use day | week | month | quarter | year.`);
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
const histogram = { field: esField, calendar_interval: calendarInterval, time_zone: row.timeZone || timezone };
|
|
1001
|
+
if (sortOrder) histogram.order = sortOrder;
|
|
1002
|
+
return { date_histogram: histogram };
|
|
1003
|
+
}
|
|
1004
|
+
var summaryFilterLists = /* @__PURE__ */ new Map();
|
|
1005
|
+
var DATE_DIFF_DIVISOR = {
|
|
1006
|
+
days: "86400000.0",
|
|
1007
|
+
hours: "3600000.0",
|
|
1008
|
+
minutes: "60000.0"
|
|
1009
|
+
};
|
|
1010
|
+
var LITERAL_DATE = /^\d{4}-\d{2}-\d{2}([T ][\d:.]+(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
1011
|
+
function getDateDiffFixedEnd(end) {
|
|
1012
|
+
if (typeof end !== "string") return null;
|
|
1013
|
+
if (dateFormulas.includes(end)) return dayjs4__default.default(getDate(end)).valueOf();
|
|
1014
|
+
if (LITERAL_DATE.test(end.trim())) {
|
|
1015
|
+
const d = dayjs4__default.default(end.trim());
|
|
1016
|
+
if (d.isValid()) return d.valueOf();
|
|
1017
|
+
}
|
|
1018
|
+
if (/^\{@\[.*\]\}$/.test(end)) {
|
|
1019
|
+
console.warn(`[@zestl/zvolv-widgets] dateDiff end "${end}" is not a known date token (${dateFormulas.join(", ")}); it is being queried as a field name.`);
|
|
1020
|
+
}
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
function getDateDiffScript(dd, fixedEnd) {
|
|
1024
|
+
const divisor = DATE_DIFF_DIVISOR[dd.unit] ?? DATE_DIFF_DIVISOR.days;
|
|
1025
|
+
if (fixedEnd != null) {
|
|
1026
|
+
return {
|
|
1027
|
+
source: `if(doc['${dd.start}'].size()==0) { return null; } return (params.now - doc['${dd.start}'].value.millis) / ${divisor};`,
|
|
1028
|
+
lang: "painless",
|
|
1029
|
+
params: { now: fixedEnd }
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
return {
|
|
1033
|
+
source: `if(doc['${dd.start}'].size()==0 || doc['${dd.end}'].size()==0) { return null; } return (doc['${dd.end}'].value.millis - doc['${dd.start}'].value.millis) / ${divisor};`,
|
|
1034
|
+
lang: "painless"
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
function getSummaryFilterClause(sumary, f, loggedInUser) {
|
|
1038
|
+
if (f.operator === "_ALL" || f.operator === "MULTIPLE") return null;
|
|
1039
|
+
const fb = {
|
|
1040
|
+
header: sumary.title,
|
|
1041
|
+
masterfield: f.operand ?? f.masterField,
|
|
1042
|
+
masterFieldDataType: f.masterFieldDataType,
|
|
1043
|
+
masterFieldType: f.masterFieldType,
|
|
1044
|
+
filter: { operator: f.operator, operand: Array.isArray(f.value) ? [...f.value] : f.value }
|
|
1045
|
+
};
|
|
1046
|
+
return getFilters(fb, "PIVOT", loggedInUser) ?? null;
|
|
1047
|
+
}
|
|
1048
|
+
function buildExtendedSummary(sumary, summaryAggs, loggedInUser, sortKeys = {}) {
|
|
1049
|
+
const name = sumary.value;
|
|
1050
|
+
const list = summaryFilterLists.get(name) ?? [];
|
|
1051
|
+
const andGroup = list.length > 1 && list.some((f) => f?.combine === "AND");
|
|
1052
|
+
const isPercentiles = sumary.type === "percentiles";
|
|
1053
|
+
if (!sumary.dateDiff && !andGroup && !(isPercentiles && sumary.percents?.length)) return false;
|
|
1054
|
+
const must = [];
|
|
1055
|
+
const mustNot = [];
|
|
1056
|
+
const conditions = andGroup ? list : list.slice(-1);
|
|
1057
|
+
for (const f of conditions) {
|
|
1058
|
+
const clause = getSummaryFilterClause(sumary, f, loggedInUser);
|
|
1059
|
+
if (!clause) continue;
|
|
1060
|
+
if (mustNotOp.includes(f.operator)) mustNot.push({ ...clause });
|
|
1061
|
+
else must.push(clause);
|
|
1062
|
+
}
|
|
1063
|
+
let metric;
|
|
1064
|
+
if (sumary.dateDiff) {
|
|
1065
|
+
const dd = sumary.dateDiff;
|
|
1066
|
+
const fixedEnd = getDateDiffFixedEnd(dd.end);
|
|
1067
|
+
must.push({ exists: { field: dd.start } });
|
|
1068
|
+
if (fixedEnd == null) must.push({ exists: { field: dd.end } });
|
|
1069
|
+
metric = { script: getDateDiffScript(dd, fixedEnd) };
|
|
1070
|
+
} else {
|
|
1071
|
+
const esField = pivotEsField(sumary);
|
|
1072
|
+
let aggType = sumary.type;
|
|
1073
|
+
let aggField = esField;
|
|
1074
|
+
const isTextField = sumary.masterFieldDataType === "STRING" && !dateTypes.includes(sumary.masterFieldType) && !isNumericTaskField(sumary);
|
|
1075
|
+
if (sumary.masterFieldType === "FORM_SEARCH" && (aggType === "value_count" || aggType === "cardinality")) {
|
|
1076
|
+
aggField = resolveFormSearchViewField(esField);
|
|
1077
|
+
} else if (sumary.masterFieldType === "FILE_UPLOAD_V2") {
|
|
1078
|
+
aggField = `${esField}.mediaKey.keyword`;
|
|
1079
|
+
} else if (isTextField) {
|
|
1080
|
+
if (["sum", "avg", "min", "max"].includes(aggType)) aggType = "value_count";
|
|
1081
|
+
aggField = `${esField}.keyword`;
|
|
1082
|
+
}
|
|
1083
|
+
metric = { field: aggField };
|
|
1084
|
+
finish(name, summaryAggs, must, mustNot, { [aggType]: metric });
|
|
1085
|
+
if (isPercentiles && sumary.percents?.length) metric.percents = sumary.percents;
|
|
1086
|
+
if (!isPercentiles && aggField === esField && sortKeys[esField]) {
|
|
1087
|
+
summaryAggs[`${name}_sort`] = { bucket_sort: { sort: [{ [name]: { order: sortKeys[esField] } }] } };
|
|
1088
|
+
}
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1091
|
+
if (isPercentiles && sumary.percents?.length) metric.percents = sumary.percents;
|
|
1092
|
+
return finish(name, summaryAggs, must, mustNot, { [sumary.type]: metric });
|
|
1093
|
+
}
|
|
1094
|
+
function finish(name, summaryAggs, must, mustNot, metric) {
|
|
1095
|
+
if (must.length === 0 && mustNot.length === 0) {
|
|
1096
|
+
summaryAggs[name] = metric;
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
const bool = {};
|
|
1100
|
+
if (must.length) bool.must = must;
|
|
1101
|
+
if (mustNot.length) bool.must_not = mustNot;
|
|
1102
|
+
summaryAggs[name] = { filter: { bool }, aggs: { [name]: metric } };
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
function groupPivotFilters(pivotFilters) {
|
|
1106
|
+
const out = /* @__PURE__ */ new Map();
|
|
1107
|
+
(pivotFilters || []).forEach((f) => {
|
|
1108
|
+
if (!f) return;
|
|
1109
|
+
const list = out.get(f.field) ?? [];
|
|
1110
|
+
list.push(f);
|
|
1111
|
+
out.set(f.field, list);
|
|
1112
|
+
});
|
|
1113
|
+
return out;
|
|
1114
|
+
}
|
|
977
1115
|
function getCompositeAggregations(pivotBody, filterMap, aggDataPoints, size = 5e4, scriptFields = {}, loggedInUser = null, sort = [], fieldMetaByName) {
|
|
978
1116
|
const composite = { size, sources: [] };
|
|
979
1117
|
pivotBody.rows.forEach((row) => {
|
|
@@ -995,17 +1133,21 @@ function getCompositeAggregations(pivotBody, filterMap, aggDataPoints, size = 5e
|
|
|
995
1133
|
const meta = fieldMetaByName?.get(row.title) || fieldMetaByName?.get(row.masterField) || {};
|
|
996
1134
|
const mfType = row.masterFieldType ?? meta.masterFieldType;
|
|
997
1135
|
const mfDataType = row.masterFieldDataType ?? meta.masterFieldDataType ?? (mfType ? getDataType(mfType) : void 0);
|
|
1136
|
+
const esField = pivotEsField(row);
|
|
998
1137
|
const sourceObj = {};
|
|
999
|
-
|
|
1000
|
-
|
|
1138
|
+
const histogram = row.interval ? getDateHistogramSource(row, esField, mfType, meta, sortOrder) : null;
|
|
1139
|
+
if (histogram) {
|
|
1140
|
+
sourceObj[row.title] = histogram;
|
|
1141
|
+
} else if (mfType === "FORM_SEARCH" || mfType === "INPUT_TAG") {
|
|
1142
|
+
sourceObj[row.title] = { terms: { field: resolveFormSearchViewField(esField) } };
|
|
1001
1143
|
} else if (mfType === "FILE_UPLOAD_V2") {
|
|
1002
|
-
sourceObj[row.title] = { terms: { field: `${
|
|
1144
|
+
sourceObj[row.title] = { terms: { field: `${esField}.mediaKey.keyword` } };
|
|
1003
1145
|
} else if (mfDataType === "STRING" && !dateTypes.includes(mfType) && !isNumericTaskField(row, fieldMetaByName)) {
|
|
1004
|
-
sourceObj[row.title] = { terms: { field: `${
|
|
1146
|
+
sourceObj[row.title] = { terms: { field: `${esField}.keyword` } };
|
|
1005
1147
|
} else if (!mfDataType && Object.keys(scriptFields).length > 0) {
|
|
1006
1148
|
sourceObj[row.title] = { terms: scriptFields[row.title] };
|
|
1007
1149
|
} else {
|
|
1008
|
-
sourceObj[row.title] = { terms: { field:
|
|
1150
|
+
sourceObj[row.title] = { terms: { field: esField } };
|
|
1009
1151
|
if (sortOrder) sourceObj[row.title]["terms"]["order"] = sortOrder;
|
|
1010
1152
|
}
|
|
1011
1153
|
composite.sources.push(sourceObj);
|
|
@@ -1023,7 +1165,7 @@ function getColumnAggregations(pivotBody, filterMap, aggDataPoints, loggedInUser
|
|
|
1023
1165
|
const colFilter = getFilters(fb, "PIVOT", loggedInUser);
|
|
1024
1166
|
if (colFilter) colFilters.push(colFilter);
|
|
1025
1167
|
}
|
|
1026
|
-
colAggs[col.title] = { terms: { field: col
|
|
1168
|
+
colAggs[col.title] = { terms: { field: pivotEsField(col) } };
|
|
1027
1169
|
if (i === pivotBody.columns.length - 1) {
|
|
1028
1170
|
const summaryResult = getSummaryAggregations(pivotBody, filterMap, aggDataPoints, loggedInUser, fMap);
|
|
1029
1171
|
colAggs[col.title]["aggs"] = summaryResult.summaryAggs;
|
|
@@ -1056,6 +1198,7 @@ function getSummaryAggregations(pivotBody, filterMap, aggDataPoints, loggedInUse
|
|
|
1056
1198
|
pivotBody.summary.forEach((sumary) => {
|
|
1057
1199
|
const filter = filterMap.get(sumary.value);
|
|
1058
1200
|
aggDataPoints["summary"].push(sumary.value);
|
|
1201
|
+
if (buildExtendedSummary(sumary, summaryAggs, loggedInUser, sortKeys)) return;
|
|
1059
1202
|
summaryAggs[sumary.value] = {};
|
|
1060
1203
|
const addBucketSort = (key) => {
|
|
1061
1204
|
if (sortKeys[key]) {
|
|
@@ -1063,17 +1206,18 @@ function getSummaryAggregations(pivotBody, filterMap, aggDataPoints, loggedInUse
|
|
|
1063
1206
|
}
|
|
1064
1207
|
};
|
|
1065
1208
|
const isTextField = sumary.masterFieldDataType === "STRING" && !dateTypes.includes(sumary.masterFieldType) && !isNumericTaskField(sumary, fMap);
|
|
1209
|
+
const esField = pivotEsField(sumary);
|
|
1066
1210
|
let aggType = sumary.type;
|
|
1067
|
-
let aggField =
|
|
1211
|
+
let aggField = esField;
|
|
1068
1212
|
if (sumary.masterFieldType === "FORM_SEARCH" && (sumary.type === "value_count" || sumary.type === "cardinality")) {
|
|
1069
|
-
aggField = resolveFormSearchViewField(
|
|
1213
|
+
aggField = resolveFormSearchViewField(esField);
|
|
1070
1214
|
} else if (sumary.masterFieldType === "FILE_UPLOAD_V2") {
|
|
1071
|
-
aggField = `${
|
|
1215
|
+
aggField = `${esField}.mediaKey.keyword`;
|
|
1072
1216
|
} else if (isTextField) {
|
|
1073
1217
|
if (["sum", "avg", "min", "max"].includes(sumary.type)) aggType = "value_count";
|
|
1074
|
-
aggField = `${
|
|
1218
|
+
aggField = `${esField}.keyword`;
|
|
1075
1219
|
}
|
|
1076
|
-
const usesRawField = aggField ===
|
|
1220
|
+
const usesRawField = aggField === esField;
|
|
1077
1221
|
if (filter) {
|
|
1078
1222
|
const fb = {
|
|
1079
1223
|
header: sumary.title,
|
|
@@ -1091,10 +1235,10 @@ function getSummaryAggregations(pivotBody, filterMap, aggDataPoints, loggedInUse
|
|
|
1091
1235
|
}
|
|
1092
1236
|
summaryAggs[sumary.value]["aggs"] = { [sumary.value]: {} };
|
|
1093
1237
|
summaryAggs[sumary.value]["aggs"][sumary.value][aggType] = { field: aggField };
|
|
1094
|
-
if (usesRawField) addBucketSort(
|
|
1238
|
+
if (usesRawField) addBucketSort(esField);
|
|
1095
1239
|
} else {
|
|
1096
1240
|
summaryAggs[sumary.value][aggType] = { field: aggField };
|
|
1097
|
-
if (usesRawField) addBucketSort(
|
|
1241
|
+
if (usesRawField) addBucketSort(esField);
|
|
1098
1242
|
}
|
|
1099
1243
|
});
|
|
1100
1244
|
return { summaryAggs, aggDataPoints };
|
|
@@ -1104,6 +1248,7 @@ function generateQuery(configuration, dataSource, formId, skip = null, limit = n
|
|
|
1104
1248
|
scriptFilters = [];
|
|
1105
1249
|
fieldMap = /* @__PURE__ */ new Map();
|
|
1106
1250
|
currentMappings = mappings;
|
|
1251
|
+
summaryFilterLists = /* @__PURE__ */ new Map();
|
|
1107
1252
|
let staticQueryClause = null;
|
|
1108
1253
|
const rawDataSourceQuery = dataSource?.query;
|
|
1109
1254
|
if (rawDataSourceQuery && typeof rawDataSourceQuery === "string") {
|
|
@@ -1196,6 +1341,7 @@ function generateQuery(configuration, dataSource, formId, skip = null, limit = n
|
|
|
1196
1341
|
if (dataSource?.pivot.filters?.length > 0) {
|
|
1197
1342
|
dataSource?.pivot.filters.forEach((f) => filterMap2.set(f.field, f));
|
|
1198
1343
|
}
|
|
1344
|
+
summaryFilterLists = groupPivotFilters(dataSource?.pivot.filters);
|
|
1199
1345
|
if (!isDrilldownView() && dataSource?.displayRowLimit && dataSource?.displayRowLimit !== "") {
|
|
1200
1346
|
size = dataSource?.displayRowLimit;
|
|
1201
1347
|
}
|
|
@@ -1773,16 +1919,29 @@ function taskField(header, masterFieldType = "INPUT_TEXT", options = {}) {
|
|
|
1773
1919
|
...options
|
|
1774
1920
|
};
|
|
1775
1921
|
}
|
|
1776
|
-
function groupBy(f, title = f.header) {
|
|
1922
|
+
function groupBy(f, title = f.header, options = {}) {
|
|
1777
1923
|
return {
|
|
1778
1924
|
title,
|
|
1779
1925
|
masterField: f.masterfield ?? f.taskfield ?? f.header,
|
|
1780
1926
|
masterFieldType: f.masterFieldType,
|
|
1781
|
-
masterFieldDataType: f.masterFieldDataType
|
|
1927
|
+
masterFieldDataType: f.masterFieldDataType,
|
|
1928
|
+
...options.interval ? { interval: options.interval } : {},
|
|
1929
|
+
...options.timeZone ? { timeZone: options.timeZone } : {}
|
|
1782
1930
|
};
|
|
1783
1931
|
}
|
|
1784
|
-
function
|
|
1932
|
+
function dateDiff(start, end, unit = "days") {
|
|
1933
|
+
const startField = start.masterfield ?? start.taskfield ?? start.header;
|
|
1934
|
+
const endField = typeof end === "string" ? end === "TODAY" ? "{@[TODAY]}" : end : end.masterfield ?? end.taskfield ?? end.header;
|
|
1935
|
+
const endLabel = typeof end === "string" ? end : end.header;
|
|
1785
1936
|
return {
|
|
1937
|
+
header: `${start.header} \u2192 ${endLabel} (${unit})`,
|
|
1938
|
+
masterfield: startField,
|
|
1939
|
+
masterFieldDataType: "FLOAT",
|
|
1940
|
+
dateDiff: { start: startField, end: endField, unit }
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function aggregate(f, type = "sum", outputName = f.header, options = {}) {
|
|
1944
|
+
const summary = {
|
|
1786
1945
|
title: f.header,
|
|
1787
1946
|
masterField: f.masterfield ?? f.taskfield ?? f.header,
|
|
1788
1947
|
masterFieldType: f.masterFieldType,
|
|
@@ -1790,6 +1949,36 @@ function aggregate(f, type = "sum", outputName = f.header) {
|
|
|
1790
1949
|
value: outputName,
|
|
1791
1950
|
type
|
|
1792
1951
|
};
|
|
1952
|
+
const dd = f.dateDiff;
|
|
1953
|
+
if (dd) summary.dateDiff = { ...dd };
|
|
1954
|
+
if (options.percents?.length) summary.percents = [...options.percents];
|
|
1955
|
+
if (options.where?.length) {
|
|
1956
|
+
summary.filters = options.where.map(([wf, cond]) => {
|
|
1957
|
+
const mf = wf.masterfield ?? wf.taskfield ?? wf.header;
|
|
1958
|
+
return {
|
|
1959
|
+
field: outputName,
|
|
1960
|
+
operand: mf,
|
|
1961
|
+
masterField: mf,
|
|
1962
|
+
masterFieldType: wf.masterFieldType,
|
|
1963
|
+
masterFieldDataType: wf.masterFieldDataType,
|
|
1964
|
+
operator: cond.operator,
|
|
1965
|
+
value: [...cond.operand],
|
|
1966
|
+
combine: "AND"
|
|
1967
|
+
};
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
return summary;
|
|
1971
|
+
}
|
|
1972
|
+
function hoistSummaryFilters(p) {
|
|
1973
|
+
if (!p?.summary?.some((s) => s.filters?.length)) return p;
|
|
1974
|
+
const hoisted = [];
|
|
1975
|
+
const summary = p.summary.map((s) => {
|
|
1976
|
+
if (!s.filters) return s;
|
|
1977
|
+
const { filters: filters2, ...rest } = s;
|
|
1978
|
+
hoisted.push(...filters2);
|
|
1979
|
+
return rest;
|
|
1980
|
+
});
|
|
1981
|
+
return { ...p, summary, filters: [...p.filters ?? [], ...hoisted] };
|
|
1793
1982
|
}
|
|
1794
1983
|
function derive(title, operator, operands) {
|
|
1795
1984
|
return { title, operator, operands };
|
|
@@ -1802,7 +1991,7 @@ function formSource(formId, fields, options = {}) {
|
|
|
1802
1991
|
formId,
|
|
1803
1992
|
newFormFramework: true,
|
|
1804
1993
|
fields: fields.map((f) => ({ ...f, formID: f.formID ?? formId })),
|
|
1805
|
-
...options.pivot ? { pivot: options.pivot } : {},
|
|
1994
|
+
...options.pivot ? { pivot: hoistSummaryFilters(options.pivot) } : {},
|
|
1806
1995
|
...options.displayRowLimit ? { displayRowLimit: options.displayRowLimit } : {}
|
|
1807
1996
|
};
|
|
1808
1997
|
}
|
|
@@ -1811,7 +2000,7 @@ function taskSource(fields, options = {}) {
|
|
|
1811
2000
|
isTask: true,
|
|
1812
2001
|
newFormFramework: true,
|
|
1813
2002
|
fields: fields.map((f) => ({ ...f, isTaskField: true })),
|
|
1814
|
-
...options.pivot ? { pivot: options.pivot } : {},
|
|
2003
|
+
...options.pivot ? { pivot: hoistSummaryFilters(options.pivot) } : {},
|
|
1815
2004
|
...options.displayRowLimit ? { displayRowLimit: options.displayRowLimit } : {}
|
|
1816
2005
|
};
|
|
1817
2006
|
}
|
|
@@ -1819,13 +2008,13 @@ function automationSource(automationId, fields = []) {
|
|
|
1819
2008
|
return { automation: { automationId, enabled: true }, fields };
|
|
1820
2009
|
}
|
|
1821
2010
|
function pivot(config) {
|
|
1822
|
-
return {
|
|
2011
|
+
return hoistSummaryFilters({
|
|
1823
2012
|
rows: config.rows ?? [],
|
|
1824
2013
|
columns: config.columns ?? [],
|
|
1825
2014
|
summary: config.summary ?? [],
|
|
1826
2015
|
filters: config.filters ?? [],
|
|
1827
2016
|
postOperations: config.postOperations ?? []
|
|
1828
|
-
};
|
|
2017
|
+
});
|
|
1829
2018
|
}
|
|
1830
2019
|
|
|
1831
2020
|
// src/core/config.ts
|
|
@@ -1923,7 +2112,7 @@ function coerceForField(field2, values) {
|
|
|
1923
2112
|
});
|
|
1924
2113
|
}
|
|
1925
2114
|
var FMT = "YYYY-MM-DD";
|
|
1926
|
-
function resolveDateToken(token, now =
|
|
2115
|
+
function resolveDateToken(token, now = dayjs4__default.default()) {
|
|
1927
2116
|
switch (String(token).toUpperCase()) {
|
|
1928
2117
|
case "TODAY":
|
|
1929
2118
|
return [now.format(FMT), now.format(FMT)];
|
|
@@ -1967,7 +2156,7 @@ function parseStrict(text2, fmt2) {
|
|
|
1967
2156
|
const y = map.YYYY, m = map.MM, d = map.DD;
|
|
1968
2157
|
if (!y || y.length !== 4 || !m || !d) return [null, null, null];
|
|
1969
2158
|
const mm = m.padStart(2, "0"), dd = d.padStart(2, "0");
|
|
1970
|
-
const parsed =
|
|
2159
|
+
const parsed = dayjs4__default.default(`${y}-${mm}-${dd}`);
|
|
1971
2160
|
if (!parsed.isValid() || parsed.format(FMT) !== `${y}-${mm}-${dd}`) return [null, null, null];
|
|
1972
2161
|
return [y, mm, dd];
|
|
1973
2162
|
}
|
|
@@ -2066,7 +2255,7 @@ function toComparable(v) {
|
|
|
2066
2255
|
if (typeof v === "number") return v;
|
|
2067
2256
|
const n = Number(v);
|
|
2068
2257
|
if (!isNaN(n)) return n;
|
|
2069
|
-
const d =
|
|
2258
|
+
const d = dayjs4__default.default(v);
|
|
2070
2259
|
return d.isValid() ? d.valueOf() : null;
|
|
2071
2260
|
}
|
|
2072
2261
|
function applyLocalSearch(rows, search, toLabel) {
|
|
@@ -2134,7 +2323,7 @@ function matchesWidgetFilter(row, f) {
|
|
|
2134
2323
|
case "DATE_IS": {
|
|
2135
2324
|
const range = resolveDateToken(String(values[0] ?? ""));
|
|
2136
2325
|
if (!range || raw2 == null || raw2 === "") return false;
|
|
2137
|
-
const d =
|
|
2326
|
+
const d = dayjs4__default.default(raw2).format(FMT);
|
|
2138
2327
|
return d >= range[0] && d <= range[1];
|
|
2139
2328
|
}
|
|
2140
2329
|
default:
|
|
@@ -2338,22 +2527,22 @@ function resolveWidgetDefaults(tokens, widgetType, config) {
|
|
|
2338
2527
|
const typeDefaults = widgetType && (defaults[widgetType] ?? defaults[widgetType.toUpperCase()]) || {};
|
|
2339
2528
|
return { ...typeDefaults, ...config || {} };
|
|
2340
2529
|
}
|
|
2341
|
-
var TokensContext =
|
|
2530
|
+
var TokensContext = React14.createContext(defaultTokens);
|
|
2342
2531
|
var ZvolvThemeProvider = ({ tokens, children }) => {
|
|
2343
|
-
const value =
|
|
2532
|
+
const value = React14.useMemo(() => mergeTokens(defaultTokens, tokens), [tokens]);
|
|
2344
2533
|
return /* @__PURE__ */ jsxRuntime.jsx(TokensContext.Provider, { value, children });
|
|
2345
2534
|
};
|
|
2346
2535
|
function useTokens() {
|
|
2347
|
-
return
|
|
2536
|
+
return React14.useContext(TokensContext);
|
|
2348
2537
|
}
|
|
2349
|
-
var TemplateContext =
|
|
2538
|
+
var TemplateContext = React14.createContext({});
|
|
2350
2539
|
var TemplateProvider = ({ templates, children }) => {
|
|
2351
|
-
const parent =
|
|
2352
|
-
const value =
|
|
2540
|
+
const parent = React14.useContext(TemplateContext);
|
|
2541
|
+
const value = React14.useMemo(() => ({ ...parent, ...templates || {} }), [parent, templates]);
|
|
2353
2542
|
return /* @__PURE__ */ jsxRuntime.jsx(TemplateContext.Provider, { value, children });
|
|
2354
2543
|
};
|
|
2355
2544
|
function useTemplateOverride(widgetType, templateId) {
|
|
2356
|
-
const map =
|
|
2545
|
+
const map = React14.useContext(TemplateContext);
|
|
2357
2546
|
return map[`${widgetType}:${templateId}`] ?? map[`${widgetType}:*`] ?? null;
|
|
2358
2547
|
}
|
|
2359
2548
|
var Label = ({ children }) => {
|
|
@@ -2553,7 +2742,7 @@ var KPI_DEFAULT_TEMPLATES = {
|
|
|
2553
2742
|
hero_chart: HeroChart,
|
|
2554
2743
|
icon: Icon
|
|
2555
2744
|
};
|
|
2556
|
-
var WidgetStateSlot =
|
|
2745
|
+
var WidgetStateSlot = React14__default.default.createContext(null);
|
|
2557
2746
|
var WidgetShell = ({
|
|
2558
2747
|
widgetType,
|
|
2559
2748
|
title,
|
|
@@ -2571,7 +2760,7 @@ var WidgetShell = ({
|
|
|
2571
2760
|
}) => {
|
|
2572
2761
|
const t = useTokens();
|
|
2573
2762
|
const chrome = resolveChrome(t, widgetType);
|
|
2574
|
-
const renderState =
|
|
2763
|
+
const renderState = React14__default.default.useContext(WidgetStateSlot);
|
|
2575
2764
|
const frame = {
|
|
2576
2765
|
display: "flex",
|
|
2577
2766
|
flexDirection: "column",
|
|
@@ -2676,24 +2865,91 @@ function alignSummaryToSeries(dataSource, series) {
|
|
|
2676
2865
|
});
|
|
2677
2866
|
return { ...dataSource, pivot: { ...dataSource.pivot, summary: fixedSummary } };
|
|
2678
2867
|
}
|
|
2868
|
+
dayjs4__default.default.extend(utc__default.default);
|
|
2869
|
+
dayjs4__default.default.extend(timezone2__default.default);
|
|
2679
2870
|
function isLikelyTimestamp(v) {
|
|
2680
2871
|
if (typeof v !== "number" || !Number.isFinite(v)) return false;
|
|
2681
2872
|
return v > 946e9 && v < 41e11;
|
|
2682
2873
|
}
|
|
2683
|
-
function
|
|
2874
|
+
function wallStartOfPeriod(d, unit) {
|
|
2875
|
+
switch (unit) {
|
|
2876
|
+
case "week": {
|
|
2877
|
+
const day = d.startOf("day");
|
|
2878
|
+
return day.subtract((day.day() + 6) % 7, "day");
|
|
2879
|
+
}
|
|
2880
|
+
case "quarter": {
|
|
2881
|
+
const month = d.startOf("month");
|
|
2882
|
+
return month.month(Math.floor(month.month() / 3) * 3);
|
|
2883
|
+
}
|
|
2884
|
+
default:
|
|
2885
|
+
return d.startOf(unit);
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
function wallAddPeriod(d, unit) {
|
|
2889
|
+
return unit === "quarter" ? d.add(3, "month") : d.add(1, unit);
|
|
2890
|
+
}
|
|
2891
|
+
function inZone(wall, d, timeZone) {
|
|
2892
|
+
const date = wall(dayjs4__default.default.utc(d.tz(timeZone).format("YYYY-MM-DD"))).format("YYYY-MM-DD");
|
|
2893
|
+
return dayjs4__default.default.tz(date, timeZone);
|
|
2894
|
+
}
|
|
2895
|
+
function startOfPeriod(d, unit, timeZone) {
|
|
2896
|
+
return timeZone ? inZone((w) => wallStartOfPeriod(w, unit), d, timeZone) : wallStartOfPeriod(d, unit);
|
|
2897
|
+
}
|
|
2898
|
+
function addPeriod(d, unit, timeZone) {
|
|
2899
|
+
return timeZone ? inZone((w) => wallAddPeriod(w, unit), d, timeZone) : wallAddPeriod(d, unit);
|
|
2900
|
+
}
|
|
2901
|
+
var HAS_OFFSET = /(Z|[+-]\d{2}:?\d{2})$/i;
|
|
2902
|
+
function parseDateValue(v, timeZone) {
|
|
2903
|
+
if (timeZone && typeof v === "string" && !HAS_OFFSET.test(v.trim())) {
|
|
2904
|
+
try {
|
|
2905
|
+
return dayjs4__default.default.tz(v, timeZone);
|
|
2906
|
+
} catch {
|
|
2907
|
+
return dayjs4__default.default(NaN);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
return dayjs4__default.default(v);
|
|
2911
|
+
}
|
|
2912
|
+
function sumInto(target, row, series) {
|
|
2913
|
+
series.forEach(({ valueField }) => {
|
|
2914
|
+
if (!(valueField in row) && !(valueField in target)) return;
|
|
2915
|
+
const a = Number(target[valueField]);
|
|
2916
|
+
const b = Number(row[valueField]);
|
|
2917
|
+
target[valueField] = (Number.isFinite(a) ? a : 0) + (Number.isFinite(b) ? b : 0);
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
function fillDateGaps({
|
|
2921
|
+
rows,
|
|
2922
|
+
xKey,
|
|
2923
|
+
series,
|
|
2924
|
+
unit,
|
|
2925
|
+
range,
|
|
2926
|
+
merge = "sum",
|
|
2927
|
+
xFormat,
|
|
2928
|
+
timeZone
|
|
2929
|
+
}) {
|
|
2684
2930
|
if (!range) return rows;
|
|
2685
|
-
const keyFormat = unit === "month" ? "YYYY-MM" : "YYYY-MM-DD";
|
|
2686
2931
|
const byPeriod = /* @__PURE__ */ new Map();
|
|
2932
|
+
const copied = /* @__PURE__ */ new Set();
|
|
2687
2933
|
rows.forEach((row) => {
|
|
2688
|
-
const d =
|
|
2689
|
-
if (d.isValid())
|
|
2934
|
+
const d = parseDateValue(row[xKey], timeZone);
|
|
2935
|
+
if (!d.isValid()) return;
|
|
2936
|
+
const key = startOfPeriod(d, unit, timeZone).valueOf();
|
|
2937
|
+
const existing = byPeriod.get(key);
|
|
2938
|
+
if (!existing || merge === "last") {
|
|
2939
|
+
byPeriod.set(key, row);
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
const merged = copied.has(key) ? existing : { ...existing };
|
|
2943
|
+
copied.add(key);
|
|
2944
|
+
sumInto(merged, row, series);
|
|
2945
|
+
byPeriod.set(key, merged);
|
|
2690
2946
|
});
|
|
2691
2947
|
const filled = [];
|
|
2692
|
-
|
|
2693
|
-
const
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
const existing = byPeriod.get(
|
|
2948
|
+
const useTimestamp = rows.length ? isLikelyTimestamp(rows[0]?.[xKey]) : xFormat === "timestamp";
|
|
2949
|
+
const end = startOfPeriod(range.end, unit, timeZone);
|
|
2950
|
+
let cursor = startOfPeriod(range.start, unit, timeZone);
|
|
2951
|
+
while (!cursor.isAfter(end)) {
|
|
2952
|
+
const existing = byPeriod.get(cursor.valueOf());
|
|
2697
2953
|
if (existing) {
|
|
2698
2954
|
filled.push(existing);
|
|
2699
2955
|
} else {
|
|
@@ -2705,10 +2961,33 @@ function fillDateGaps({ rows, xKey, series, unit, range }) {
|
|
|
2705
2961
|
});
|
|
2706
2962
|
filled.push(zeroRow);
|
|
2707
2963
|
}
|
|
2708
|
-
cursor = cursor
|
|
2964
|
+
cursor = addPeriod(cursor, unit, timeZone);
|
|
2709
2965
|
}
|
|
2710
2966
|
return filled;
|
|
2711
2967
|
}
|
|
2968
|
+
function applyCumulative(rows, xKey, fields, baseline = {}) {
|
|
2969
|
+
if (!fields.length) return rows;
|
|
2970
|
+
const dated = rows.map((row, i) => ({ row, i, t: dayjs4__default.default(row?.[xKey]).valueOf() }));
|
|
2971
|
+
dated.sort((a, b) => {
|
|
2972
|
+
const at = Number.isFinite(a.t), bt = Number.isFinite(b.t);
|
|
2973
|
+
if (at && bt) return a.t - b.t || a.i - b.i;
|
|
2974
|
+
if (at !== bt) return at ? -1 : 1;
|
|
2975
|
+
return a.i - b.i;
|
|
2976
|
+
});
|
|
2977
|
+
const running = {};
|
|
2978
|
+
fields.forEach((f) => {
|
|
2979
|
+
running[f] = Number(baseline[f]) || 0;
|
|
2980
|
+
});
|
|
2981
|
+
return dated.map(({ row }) => {
|
|
2982
|
+
const next = { ...row };
|
|
2983
|
+
fields.forEach((f) => {
|
|
2984
|
+
const v = Number(row?.[f]);
|
|
2985
|
+
running[f] += Number.isFinite(v) ? v : 0;
|
|
2986
|
+
next[f] = running[f];
|
|
2987
|
+
});
|
|
2988
|
+
return next;
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2712
2991
|
function toNumber(v) {
|
|
2713
2992
|
if (typeof v === "number") return Number.isFinite(v) ? v : 0;
|
|
2714
2993
|
const n = Number(v);
|
|
@@ -2726,6 +3005,49 @@ function coerceSeriesNumbers(rows, series) {
|
|
|
2726
3005
|
});
|
|
2727
3006
|
}
|
|
2728
3007
|
|
|
3008
|
+
// src/transforms/percentiles.ts
|
|
3009
|
+
function percentileKey(name, percent) {
|
|
3010
|
+
return `${name}_p${String(Number(percent)).replace(".", "_")}`;
|
|
3011
|
+
}
|
|
3012
|
+
function percentileSummaries(summary) {
|
|
3013
|
+
return (summary || []).filter((s) => s?.type === "percentiles" && s.value).map((s) => s.value);
|
|
3014
|
+
}
|
|
3015
|
+
function readPercentiles(raw2) {
|
|
3016
|
+
if (raw2 == null || typeof raw2 !== "object") return null;
|
|
3017
|
+
const values = Array.isArray(raw2) ? raw2 : raw2.values ?? raw2;
|
|
3018
|
+
const out = {};
|
|
3019
|
+
if (Array.isArray(values)) {
|
|
3020
|
+
values.forEach((v) => {
|
|
3021
|
+
if (v && v.key != null) out[String(v.key)] = v.value;
|
|
3022
|
+
});
|
|
3023
|
+
} else if (typeof values === "object") {
|
|
3024
|
+
for (const [k, v] of Object.entries(values)) {
|
|
3025
|
+
if (k.endsWith("_as_string")) continue;
|
|
3026
|
+
if (Number.isFinite(Number(k))) out[k] = v;
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
return Object.keys(out).length ? out : null;
|
|
3030
|
+
}
|
|
3031
|
+
function flattenPercentiles(rows, names) {
|
|
3032
|
+
if (!names.length) return rows;
|
|
3033
|
+
return rows.map((row) => {
|
|
3034
|
+
let next = row;
|
|
3035
|
+
for (const name of names) {
|
|
3036
|
+
const parsed = readPercentiles(row?.[name]);
|
|
3037
|
+
if (!parsed) continue;
|
|
3038
|
+
if (next === row) next = { ...row };
|
|
3039
|
+
let median;
|
|
3040
|
+
for (const [percent, value] of Object.entries(parsed)) {
|
|
3041
|
+
const n = value == null ? null : Number(value);
|
|
3042
|
+
next[percentileKey(name, percent)] = n;
|
|
3043
|
+
if (Number(percent) === 50 && n != null) median = n;
|
|
3044
|
+
}
|
|
3045
|
+
if (median !== void 0) next[name] = median;
|
|
3046
|
+
}
|
|
3047
|
+
return next;
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
|
|
2729
3051
|
// src/react/useWidgetData.ts
|
|
2730
3052
|
var ALIGN_TYPES = /* @__PURE__ */ new Set(["BAR", "BAR_CHART", "PIE", "PIE_CHART", "DOUGHNUT", "DOUGHNUT_CHART"]);
|
|
2731
3053
|
var formCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -2746,9 +3068,30 @@ function getFormOnce(client, formId) {
|
|
|
2746
3068
|
}
|
|
2747
3069
|
return p;
|
|
2748
3070
|
}
|
|
3071
|
+
function baselineClause(source, start) {
|
|
3072
|
+
const f = (source.dataSource?.fields || []).find(
|
|
3073
|
+
(x) => x?.header === source.dateField || x?.masterfield === source.dateField
|
|
3074
|
+
);
|
|
3075
|
+
return {
|
|
3076
|
+
header: f?.header ?? source.dateField,
|
|
3077
|
+
masterfield: f?.masterfield ?? source.dateField,
|
|
3078
|
+
masterFieldType: f?.masterFieldType ?? "DATE_PICKER",
|
|
3079
|
+
masterFieldDataType: f?.masterFieldDataType ?? "STRING",
|
|
3080
|
+
// Date-only: the query generator keeps "YYYY-MM-DD" operands as dates.
|
|
3081
|
+
filter: { operator: "LESS_THAN", operand: [start.format("YYYY-MM-DD")] }
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
function readBaseline(rows) {
|
|
3085
|
+
const out = {};
|
|
3086
|
+
for (const [k, v] of Object.entries(rows?.[0] ?? {})) {
|
|
3087
|
+
const n = Number(v);
|
|
3088
|
+
if (Number.isFinite(n)) out[k] = n;
|
|
3089
|
+
}
|
|
3090
|
+
return out;
|
|
3091
|
+
}
|
|
2749
3092
|
function useDebounced(value, ms, enabled) {
|
|
2750
|
-
const [v, setV] =
|
|
2751
|
-
|
|
3093
|
+
const [v, setV] = React14.useState(value);
|
|
3094
|
+
React14.useEffect(() => {
|
|
2752
3095
|
if (!enabled) {
|
|
2753
3096
|
setV(value);
|
|
2754
3097
|
return;
|
|
@@ -2797,23 +3140,23 @@ function useWidgetData(args) {
|
|
|
2797
3140
|
searchDebounceMs = 400
|
|
2798
3141
|
} = args;
|
|
2799
3142
|
const rt = useZvolvRuntime();
|
|
2800
|
-
const [sourceRows, setSourceRows] =
|
|
2801
|
-
const [userLabels, setUserLabels] =
|
|
2802
|
-
const [initialLoading, setInitialLoading] =
|
|
2803
|
-
const [loading, setLoading] =
|
|
2804
|
-
const [error, setError] =
|
|
2805
|
-
const [nonce, setNonce] =
|
|
2806
|
-
const requestedUserIds =
|
|
2807
|
-
const reqSeq =
|
|
2808
|
-
const config =
|
|
2809
|
-
const series =
|
|
2810
|
-
const xKey =
|
|
2811
|
-
const configured =
|
|
3143
|
+
const [sourceRows, setSourceRows] = React14.useState([]);
|
|
3144
|
+
const [userLabels, setUserLabels] = React14.useState({});
|
|
3145
|
+
const [initialLoading, setInitialLoading] = React14.useState(true);
|
|
3146
|
+
const [loading, setLoading] = React14.useState(false);
|
|
3147
|
+
const [error, setError] = React14.useState(null);
|
|
3148
|
+
const [nonce, setNonce] = React14.useState(0);
|
|
3149
|
+
const requestedUserIds = React14.useRef(/* @__PURE__ */ new Set());
|
|
3150
|
+
const reqSeq = React14.useRef(0);
|
|
3151
|
+
const config = React14.useMemo(() => parseConfig(configurations), [configurations]);
|
|
3152
|
+
const series = React14.useMemo(() => resolveSeries(config), [config]);
|
|
3153
|
+
const xKey = React14.useMemo(() => resolveXKey(series), [series]);
|
|
3154
|
+
const configured = React14.useMemo(() => isDataSourceConfigured(dataSource), [dataSource]);
|
|
2812
3155
|
const hasAutomation = !!automationRows;
|
|
2813
3156
|
const tableMode = resolveTableMode(dataSource, widgetType, hasAutomation);
|
|
2814
3157
|
const filterMode = resolveFilterMode(dataSource, hasAutomation);
|
|
2815
3158
|
const debouncedSearch = useDebounced(search, searchDebounceMs, tableMode === "remote");
|
|
2816
|
-
const remote =
|
|
3159
|
+
const remote = React14.useMemo(() => ({
|
|
2817
3160
|
search: tableMode === "remote" ? debouncedSearch : "",
|
|
2818
3161
|
columnFilters: tableMode === "remote" ? columnFilters : void 0,
|
|
2819
3162
|
sort: tableMode === "remote" ? sort : null,
|
|
@@ -2825,14 +3168,35 @@ function useWidgetData(args) {
|
|
|
2825
3168
|
const localKey = JSON.stringify(localFilters ?? {});
|
|
2826
3169
|
const dsKey = JSON.stringify(dataSource ?? {});
|
|
2827
3170
|
const remoteKey = JSON.stringify(remote);
|
|
2828
|
-
const
|
|
2829
|
-
|
|
3171
|
+
const dateGapKey = JSON.stringify(dateGap ?? null);
|
|
3172
|
+
const gapTimeZone = React14.useMemo(() => {
|
|
3173
|
+
if (dateGap?.timeZone) return dateGap.timeZone;
|
|
3174
|
+
const row = (dataSource?.pivot?.rows || []).find((r) => r?.title === xKey && r?.interval);
|
|
3175
|
+
return row?.timeZone || void 0;
|
|
3176
|
+
}, [dateGap?.timeZone, dataSource, xKey]);
|
|
3177
|
+
const normalize = React14.useCallback((incoming, fetchedBaseline) => {
|
|
3178
|
+
const percentiles = percentileSummaries(dataSource?.pivot?.summary);
|
|
3179
|
+
const flat = percentiles.length ? flattenPercentiles(incoming, percentiles) : incoming;
|
|
3180
|
+
const withPostOps = applyPostOperations(flat, dataSource?.pivot?.postOperations);
|
|
2830
3181
|
const numeric = coerceSeriesNumbers(withPostOps, series);
|
|
2831
3182
|
if (!dateGap) return numeric;
|
|
2832
|
-
const
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
3183
|
+
const tz = gapTimeZone;
|
|
3184
|
+
const period = (v) => startOfPeriod(parseDateValue(v, tz), dateGap.unit, tz);
|
|
3185
|
+
const range = dateGap.start && dateGap.end ? { start: period(dateGap.start), end: period(dateGap.end) } : null;
|
|
3186
|
+
const filled = fillDateGaps({ rows: numeric, xKey, series, unit: dateGap.unit, range, merge: dateGap.merge, timeZone: tz });
|
|
3187
|
+
if (!dateGap.cumulative) return filled;
|
|
3188
|
+
let inRange = filled;
|
|
3189
|
+
if (dateGap.start && !range) {
|
|
3190
|
+
const from = period(dateGap.start).valueOf();
|
|
3191
|
+
inRange = filled.filter((row) => {
|
|
3192
|
+
const d = parseDateValue(row?.[xKey], tz);
|
|
3193
|
+
return !d.isValid() || startOfPeriod(d, dateGap.unit, tz).valueOf() >= from;
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
const fields = dateGap.cumulative === true ? series.map((s) => s.valueField) : dateGap.cumulative;
|
|
3197
|
+
return applyCumulative(inRange, xKey, fields, { ...fetchedBaseline || {}, ...dateGap.baseline || {} });
|
|
3198
|
+
}, [dataSource, series, xKey, dateGap, gapTimeZone]);
|
|
3199
|
+
React14.useEffect(() => {
|
|
2836
3200
|
if (automationRows) {
|
|
2837
3201
|
if (automationLoading) {
|
|
2838
3202
|
setLoading(true);
|
|
@@ -2853,53 +3217,67 @@ function useWidgetData(args) {
|
|
|
2853
3217
|
let cancelled = false;
|
|
2854
3218
|
setError(null);
|
|
2855
3219
|
setLoading(true);
|
|
3220
|
+
const queryForm = async (ds, mode, extraClauses = []) => {
|
|
3221
|
+
const formId = ds.formId ?? ds.fields?.[0]?.formID;
|
|
3222
|
+
const withFormId = formId ? { ...ds, formId } : ds;
|
|
3223
|
+
const merged = mergeHostFilters(localFilters, dashFilters);
|
|
3224
|
+
const cfgWithDates = { ...config, cdateFilters: mode === "main" ? cdateFilters : [] };
|
|
3225
|
+
const dashboardFilters = rt.formatDashboardFilters ? [...rt.formatDashboardFilters(cfgWithDates, withFormId, merged) || []] : [];
|
|
3226
|
+
if (remote.widgetFilters?.length) {
|
|
3227
|
+
dashboardFilters.push(...buildWidgetFilterClauses(withFormId, remote.widgetFilters));
|
|
3228
|
+
}
|
|
3229
|
+
if (mode === "main" && remote.columnFilters && Object.keys(remote.columnFilters).length) {
|
|
3230
|
+
dashboardFilters.push(...rt.formatHeaderFilters ? rt.formatHeaderFilters(withFormId, remote.columnFilters) || [] : buildColumnFilterClauses(withFormId, remote.columnFilters));
|
|
3231
|
+
}
|
|
3232
|
+
const searchClause = mode === "main" ? buildSearchClause(remote.search) : null;
|
|
3233
|
+
if (searchClause) dashboardFilters.push(searchClause);
|
|
3234
|
+
dashboardFilters.push(...extraClauses);
|
|
3235
|
+
const { dashboardSort, config: queryConfig } = buildRemoteSort(config, withFormId, mode === "main" ? remote.sort : null);
|
|
3236
|
+
if (formId && !ds.isTask && rt.client?.workflow?.getSingleForm) {
|
|
3237
|
+
const form = await getFormOnce(rt.client, formId).catch((e) => {
|
|
3238
|
+
console.warn(`[@zestl/zvolv-widgets] form lookup failed for ${formId}; querying anyway`, e);
|
|
3239
|
+
return true;
|
|
3240
|
+
});
|
|
3241
|
+
if (!form) throw new Error("You don't have access to this widget's form.");
|
|
3242
|
+
}
|
|
3243
|
+
const fixedDs = mode === "main" && ALIGN_TYPES.has(String(widgetType ?? "").toUpperCase()) && hasDeclaredSeries(config) ? alignSummaryToSeries(withFormId, series) : withFormId;
|
|
3244
|
+
const loggedInUser = rt.resolveQueryUser ? await rt.resolveQueryUser() : null;
|
|
3245
|
+
const mappings = rt.prefetchQueryMappings ? await rt.prefetchQueryMappings(fixedDs.formId) : void 0;
|
|
3246
|
+
if (!rt.generateQuery) throw new Error("runtime.generateQuery is not configured");
|
|
3247
|
+
const query = rt.generateQuery(
|
|
3248
|
+
queryConfig,
|
|
3249
|
+
fixedDs,
|
|
3250
|
+
fixedDs.formId,
|
|
3251
|
+
0,
|
|
3252
|
+
rowLimit,
|
|
3253
|
+
[],
|
|
3254
|
+
dashboardFilters,
|
|
3255
|
+
[],
|
|
3256
|
+
loggedInUser,
|
|
3257
|
+
dashboardSort,
|
|
3258
|
+
null,
|
|
3259
|
+
fixedDs.isTask,
|
|
3260
|
+
mappings
|
|
3261
|
+
);
|
|
3262
|
+
const res = await rt.client.workflow.elasticSearch(query);
|
|
3263
|
+
return Array.isArray(res) ? res : res?.elements ?? res?.data ?? [];
|
|
3264
|
+
};
|
|
2856
3265
|
(async () => {
|
|
2857
3266
|
try {
|
|
2858
3267
|
const ds = dataSource || {};
|
|
2859
3268
|
let fetched = [];
|
|
3269
|
+
let fetchedBaseline;
|
|
2860
3270
|
if (usesFormFramework(ds)) {
|
|
2861
|
-
const
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
}
|
|
2872
|
-
const searchClause = buildSearchClause(remote.search);
|
|
2873
|
-
if (searchClause) dashboardFilters.push(searchClause);
|
|
2874
|
-
const { dashboardSort, config: queryConfig } = buildRemoteSort(config, withFormId, remote.sort);
|
|
2875
|
-
if (formId && !ds.isTask && rt.client?.workflow?.getSingleForm) {
|
|
2876
|
-
const form = await getFormOnce(rt.client, formId).catch((e) => {
|
|
2877
|
-
console.warn(`[@zestl/zvolv-widgets] form lookup failed for ${formId}; querying anyway`, e);
|
|
2878
|
-
return true;
|
|
2879
|
-
});
|
|
2880
|
-
if (!form) throw new Error("You don't have access to this widget's form.");
|
|
2881
|
-
}
|
|
2882
|
-
const fixedDs = ALIGN_TYPES.has(String(widgetType ?? "").toUpperCase()) && hasDeclaredSeries(config) ? alignSummaryToSeries(withFormId, series) : withFormId;
|
|
2883
|
-
const loggedInUser = rt.resolveQueryUser ? await rt.resolveQueryUser() : null;
|
|
2884
|
-
const mappings = rt.prefetchQueryMappings ? await rt.prefetchQueryMappings(fixedDs.formId) : void 0;
|
|
2885
|
-
if (!rt.generateQuery) throw new Error("runtime.generateQuery is not configured");
|
|
2886
|
-
const query = rt.generateQuery(
|
|
2887
|
-
queryConfig,
|
|
2888
|
-
fixedDs,
|
|
2889
|
-
fixedDs.formId,
|
|
2890
|
-
0,
|
|
2891
|
-
rowLimit,
|
|
2892
|
-
[],
|
|
2893
|
-
dashboardFilters,
|
|
2894
|
-
[],
|
|
2895
|
-
loggedInUser,
|
|
2896
|
-
dashboardSort,
|
|
2897
|
-
null,
|
|
2898
|
-
fixedDs.isTask,
|
|
2899
|
-
mappings
|
|
2900
|
-
);
|
|
2901
|
-
const res = await rt.client.workflow.elasticSearch(query);
|
|
2902
|
-
fetched = Array.isArray(res) ? res : res?.elements ?? res?.data ?? [];
|
|
3271
|
+
const baselineReq = dateGap?.baselineSource && dateGap.start ? queryForm(
|
|
3272
|
+
dateGap.baselineSource.dataSource,
|
|
3273
|
+
"baseline",
|
|
3274
|
+
[baselineClause(dateGap.baselineSource, startOfPeriod(parseDateValue(dateGap.start, gapTimeZone), dateGap.unit, gapTimeZone))]
|
|
3275
|
+
).catch((e) => {
|
|
3276
|
+
console.warn("[@zestl/zvolv-widgets] dateGap baseline fetch failed; starting from 0", e);
|
|
3277
|
+
return [];
|
|
3278
|
+
}) : null;
|
|
3279
|
+
fetched = await queryForm(ds, "main");
|
|
3280
|
+
if (baselineReq) fetchedBaseline = readBaseline(await baselineReq);
|
|
2903
3281
|
} else {
|
|
2904
3282
|
const wftype = config?.dataConfs?.WorkflowTypeID ?? workflowTypeId;
|
|
2905
3283
|
const res = await rt.client.workflow.getDataSource(JSON.stringify({ widgetId, wftype }));
|
|
@@ -2907,7 +3285,7 @@ function useWidgetData(args) {
|
|
|
2907
3285
|
fetched = payload?.table ?? (Array.isArray(payload) ? payload : []);
|
|
2908
3286
|
}
|
|
2909
3287
|
if (cancelled || seq !== reqSeq.current) return;
|
|
2910
|
-
setSourceRows(normalize(fetched));
|
|
3288
|
+
setSourceRows(normalize(fetched, fetchedBaseline));
|
|
2911
3289
|
} catch (err) {
|
|
2912
3290
|
if (cancelled || seq !== reqSeq.current) return;
|
|
2913
3291
|
console.error(`[@zestl/zvolv-widgets] ${widgetType ?? "widget"} fetch failed:`, err);
|
|
@@ -2923,8 +3301,8 @@ function useWidgetData(args) {
|
|
|
2923
3301
|
return () => {
|
|
2924
3302
|
cancelled = true;
|
|
2925
3303
|
};
|
|
2926
|
-
}, [configKey, dashKey, cdateKey, localKey, dsKey, remoteKey, configured, nonce, automationRows, automationLoading]);
|
|
2927
|
-
const userKind =
|
|
3304
|
+
}, [configKey, dashKey, cdateKey, localKey, dsKey, remoteKey, dateGapKey, configured, nonce, automationRows, automationLoading]);
|
|
3305
|
+
const userKind = React14.useMemo(() => {
|
|
2928
3306
|
if (!rt.getUserFieldKind) return null;
|
|
2929
3307
|
const field2 = (dataSource?.fields || []).find(
|
|
2930
3308
|
(f) => f?.masterfield === xKey || f?.header === xKey
|
|
@@ -2934,7 +3312,7 @@ function useWidgetData(args) {
|
|
|
2934
3312
|
);
|
|
2935
3313
|
return rt.getUserFieldKind(field2?.masterFieldType ?? pivotRow?.masterFieldType);
|
|
2936
3314
|
}, [rt, dataSource, xKey]);
|
|
2937
|
-
|
|
3315
|
+
React14.useEffect(() => {
|
|
2938
3316
|
if (!userKind || !rt.resolveUserLabels || sourceRows.length === 0) return;
|
|
2939
3317
|
const pending2 = sourceRows.flatMap((row) => {
|
|
2940
3318
|
const v = row?.[xKey];
|
|
@@ -2960,14 +3338,14 @@ function useWidgetData(args) {
|
|
|
2960
3338
|
cancelled = true;
|
|
2961
3339
|
};
|
|
2962
3340
|
}, [sourceRows, userKind, xKey, rt]);
|
|
2963
|
-
const toLabel =
|
|
3341
|
+
const toLabel = React14.useCallback((value) => {
|
|
2964
3342
|
if (!userKind || value === null || value === void 0 || value === "") return value;
|
|
2965
3343
|
if (Array.isArray(value)) {
|
|
2966
3344
|
return value.map((v) => userLabels[String(v)] ?? v).filter((v) => v !== "" && v != null).join(", ");
|
|
2967
3345
|
}
|
|
2968
3346
|
return userLabels[String(value)] ?? value;
|
|
2969
3347
|
}, [userKind, userLabels]);
|
|
2970
|
-
const rows =
|
|
3348
|
+
const rows = React14.useMemo(() => {
|
|
2971
3349
|
let out = sourceRows;
|
|
2972
3350
|
if (filterMode === "local" && widgetFilters?.length) out = applyLocalWidgetFilters(out, widgetFilters);
|
|
2973
3351
|
if (tableMode === "local") {
|
|
@@ -2986,7 +3364,7 @@ function useWidgetData(args) {
|
|
|
2986
3364
|
loading,
|
|
2987
3365
|
initialLoading,
|
|
2988
3366
|
error,
|
|
2989
|
-
refresh:
|
|
3367
|
+
refresh: React14.useCallback(() => setNonce((n) => n + 1), []),
|
|
2990
3368
|
meta: {
|
|
2991
3369
|
xKey,
|
|
2992
3370
|
series,
|
|
@@ -3000,10 +3378,10 @@ function useWidgetData(args) {
|
|
|
3000
3378
|
};
|
|
3001
3379
|
}
|
|
3002
3380
|
function useControllable(controlled, initial, onChange) {
|
|
3003
|
-
const [inner, setInner] =
|
|
3381
|
+
const [inner, setInner] = React14.useState(initial);
|
|
3004
3382
|
const isControlled = controlled !== void 0;
|
|
3005
3383
|
const value = isControlled ? controlled : inner;
|
|
3006
|
-
const set =
|
|
3384
|
+
const set = React14.useCallback((v) => {
|
|
3007
3385
|
if (!isControlled) setInner(v);
|
|
3008
3386
|
onChange?.(v);
|
|
3009
3387
|
}, [isControlled, onChange]);
|
|
@@ -3030,56 +3408,56 @@ function useWidgetInteractions(props = {}) {
|
|
|
3030
3408
|
props.defaultWidgetFilters ?? EMPTY_INTERACTIONS.widgetFilters,
|
|
3031
3409
|
props.onWidgetFiltersChange
|
|
3032
3410
|
);
|
|
3033
|
-
const latest =
|
|
3411
|
+
const latest = React14.useRef({ search, columnFilters, sort, widgetFilters });
|
|
3034
3412
|
latest.current = { search, columnFilters, sort, widgetFilters };
|
|
3035
3413
|
const onAny = props.onInteractionsChange;
|
|
3036
|
-
const report =
|
|
3414
|
+
const report = React14.useCallback((patch) => {
|
|
3037
3415
|
const next = { ...latest.current, ...patch };
|
|
3038
3416
|
latest.current = next;
|
|
3039
3417
|
onAny?.(next);
|
|
3040
3418
|
}, [onAny]);
|
|
3041
|
-
const setSearch =
|
|
3419
|
+
const setSearch = React14.useCallback((v) => {
|
|
3042
3420
|
setSearchRaw(v);
|
|
3043
3421
|
report({ search: v });
|
|
3044
3422
|
}, [setSearchRaw, report]);
|
|
3045
|
-
const setColumnFilters =
|
|
3423
|
+
const setColumnFilters = React14.useCallback((v) => {
|
|
3046
3424
|
setColumnFiltersRaw(v);
|
|
3047
3425
|
report({ columnFilters: v });
|
|
3048
3426
|
}, [setColumnFiltersRaw, report]);
|
|
3049
|
-
const setColumnFilter =
|
|
3427
|
+
const setColumnFilter = React14.useCallback((key, values) => {
|
|
3050
3428
|
const next = { ...latest.current.columnFilters };
|
|
3051
3429
|
if (values && values.length) next[key] = values;
|
|
3052
3430
|
else delete next[key];
|
|
3053
3431
|
setColumnFilters(next);
|
|
3054
3432
|
}, [setColumnFilters]);
|
|
3055
|
-
const setSort =
|
|
3433
|
+
const setSort = React14.useCallback((v) => {
|
|
3056
3434
|
setSortRaw(v);
|
|
3057
3435
|
report({ sort: v });
|
|
3058
3436
|
}, [setSortRaw, report]);
|
|
3059
|
-
const toggleSort =
|
|
3437
|
+
const toggleSort = React14.useCallback((key) => {
|
|
3060
3438
|
const cur = latest.current.sort;
|
|
3061
3439
|
if (!cur || cur.key !== key) setSort({ key, direction: "asc" });
|
|
3062
3440
|
else if (cur.direction === "asc") setSort({ key, direction: "desc" });
|
|
3063
3441
|
else setSort(null);
|
|
3064
3442
|
}, [setSort]);
|
|
3065
|
-
const setWidgetFilters =
|
|
3443
|
+
const setWidgetFilters = React14.useCallback((v) => {
|
|
3066
3444
|
setWidgetFiltersRaw(v);
|
|
3067
3445
|
report({ widgetFilters: v });
|
|
3068
3446
|
}, [setWidgetFiltersRaw, report]);
|
|
3069
|
-
const addWidgetFilter =
|
|
3447
|
+
const addWidgetFilter = React14.useCallback((f) => {
|
|
3070
3448
|
setWidgetFilters([...latest.current.widgetFilters, f]);
|
|
3071
3449
|
}, [setWidgetFilters]);
|
|
3072
|
-
const removeWidgetFilter =
|
|
3450
|
+
const removeWidgetFilter = React14.useCallback((index) => {
|
|
3073
3451
|
setWidgetFilters(latest.current.widgetFilters.filter((_2, i) => i !== index));
|
|
3074
3452
|
}, [setWidgetFilters]);
|
|
3075
|
-
const clearAll =
|
|
3453
|
+
const clearAll = React14.useCallback(() => {
|
|
3076
3454
|
setSearchRaw("");
|
|
3077
3455
|
setColumnFiltersRaw({});
|
|
3078
3456
|
setSortRaw(null);
|
|
3079
3457
|
setWidgetFiltersRaw([]);
|
|
3080
3458
|
report({ ...EMPTY_INTERACTIONS });
|
|
3081
3459
|
}, [setSearchRaw, setColumnFiltersRaw, setSortRaw, setWidgetFiltersRaw, report]);
|
|
3082
|
-
return
|
|
3460
|
+
return React14.useMemo(() => ({
|
|
3083
3461
|
search,
|
|
3084
3462
|
columnFilters,
|
|
3085
3463
|
sort,
|
|
@@ -3192,7 +3570,7 @@ function describeWidgetFilter(f) {
|
|
|
3192
3570
|
function useInteractiveWidgetData(props, options = {}) {
|
|
3193
3571
|
const t = useTokens();
|
|
3194
3572
|
const interactions = useWidgetInteractions(props);
|
|
3195
|
-
const dataArgs =
|
|
3573
|
+
const dataArgs = React14.useMemo(() => {
|
|
3196
3574
|
const rest = { ...props };
|
|
3197
3575
|
for (const k of UI_KEYS) delete rest[k];
|
|
3198
3576
|
return rest;
|
|
@@ -3204,7 +3582,7 @@ function useInteractiveWidgetData(props, options = {}) {
|
|
|
3204
3582
|
sort: interactions.sort,
|
|
3205
3583
|
widgetFilters: interactions.widgetFilters
|
|
3206
3584
|
});
|
|
3207
|
-
const config =
|
|
3585
|
+
const config = React14.useMemo(
|
|
3208
3586
|
() => resolveWidgetDefaults(t, props.widgetType, parseConfig(props.configurations)),
|
|
3209
3587
|
[t, props.widgetType, props.configurations]
|
|
3210
3588
|
);
|
|
@@ -3214,7 +3592,7 @@ function useInteractiveWidgetData(props, options = {}) {
|
|
|
3214
3592
|
showColumnFilter: !!options.table && (props.showColumnFilter ?? !!(config?.showColumnFilter ?? config?.styling?.showColumnFilter ?? props.renderColumnFilter)),
|
|
3215
3593
|
enableSort: !!options.table && (props.enableSort ?? config?.enableSort !== false)
|
|
3216
3594
|
};
|
|
3217
|
-
const seen =
|
|
3595
|
+
const seen = React14.useRef({ ds: "", values: /* @__PURE__ */ new Map() });
|
|
3218
3596
|
const dsKey = JSON.stringify(props.dataSource ?? {});
|
|
3219
3597
|
if (seen.current.ds !== dsKey) seen.current = { ds: dsKey, values: /* @__PURE__ */ new Map() };
|
|
3220
3598
|
const getColumnOptions = (key) => {
|
|
@@ -3232,7 +3610,7 @@ function useInteractiveWidgetData(props, options = {}) {
|
|
|
3232
3610
|
}
|
|
3233
3611
|
return [...byValue.values()];
|
|
3234
3612
|
};
|
|
3235
|
-
const fields =
|
|
3613
|
+
const fields = React14.useMemo(() => filterableFields(props.dataSource), [dsKey]);
|
|
3236
3614
|
const searchNode = flags.showSearch ? (() => {
|
|
3237
3615
|
const args = {
|
|
3238
3616
|
value: interactions.search,
|
|
@@ -3275,7 +3653,7 @@ function useInteractiveWidgetData(props, options = {}) {
|
|
|
3275
3653
|
tokens: t
|
|
3276
3654
|
};
|
|
3277
3655
|
parts.push(
|
|
3278
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3656
|
+
/* @__PURE__ */ jsxRuntime.jsx(React14__default.default.Fragment, { children: props.renderSortIndicator ? props.renderSortIndicator(args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultSortIndicator, { ...args }) }, "sort")
|
|
3279
3657
|
);
|
|
3280
3658
|
}
|
|
3281
3659
|
if (flags.showColumnFilter) {
|
|
@@ -3289,7 +3667,7 @@ function useInteractiveWidgetData(props, options = {}) {
|
|
|
3289
3667
|
tokens: t
|
|
3290
3668
|
};
|
|
3291
3669
|
parts.push(
|
|
3292
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3670
|
+
/* @__PURE__ */ jsxRuntime.jsx(React14__default.default.Fragment, { children: props.renderColumnFilter ? props.renderColumnFilter(args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultColumnFilter, { ...args }) }, "filter")
|
|
3293
3671
|
);
|
|
3294
3672
|
}
|
|
3295
3673
|
return parts.length ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: parts }) : null;
|
|
@@ -3331,9 +3709,9 @@ var DefaultSearch = ({ value, setValue, placeholder, tokens: t }) => /* @__PURE_
|
|
|
3331
3709
|
}
|
|
3332
3710
|
);
|
|
3333
3711
|
var Popover = ({ trigger, children, tokens: t, align = "right" }) => {
|
|
3334
|
-
const [open, setOpen] =
|
|
3335
|
-
const ref =
|
|
3336
|
-
|
|
3712
|
+
const [open, setOpen] = React14.useState(false);
|
|
3713
|
+
const ref = React14.useRef(null);
|
|
3714
|
+
React14.useEffect(() => {
|
|
3337
3715
|
if (!open) return;
|
|
3338
3716
|
const onDown = (e) => {
|
|
3339
3717
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -3395,7 +3773,7 @@ var DefaultSortIndicator = ({ direction, toggle, column, tokens: t }) => /* @__P
|
|
|
3395
3773
|
}
|
|
3396
3774
|
);
|
|
3397
3775
|
var DefaultColumnFilter = ({ column, selected, setSelected, clear, options, tokens: t }) => {
|
|
3398
|
-
const [q, setQ] =
|
|
3776
|
+
const [q, setQ] = React14.useState("");
|
|
3399
3777
|
const shown = options.filter((o) => !q || o.label.toLowerCase().includes(q.toLowerCase()));
|
|
3400
3778
|
const isOn = (v) => selected.some((s) => String(s) === String(v));
|
|
3401
3779
|
return /* @__PURE__ */ jsxRuntime.jsx(Popover, { tokens: t, align: "left", trigger: (_2, toggle) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3423,10 +3801,10 @@ var DefaultColumnFilter = ({ column, selected, setSelected, clear, options, toke
|
|
|
3423
3801
|
};
|
|
3424
3802
|
var DATE_TOKENS = ["TODAY", "YESTERDAY", "LAST_SEVEN_DAYS", "LAST_THIRTY_DAYS", "THIS_WEEK", "THIS_MONTH", "THIS_YEAR"];
|
|
3425
3803
|
var DefaultWidgetFilter = ({ filters: filters2, addFilter, removeFilter, clear, fields, tokens: t }) => {
|
|
3426
|
-
const [field2, setField] =
|
|
3427
|
-
const [operator, setOperator] =
|
|
3428
|
-
const [value, setValue] =
|
|
3429
|
-
const [value2, setValue2] =
|
|
3804
|
+
const [field2, setField] = React14.useState("");
|
|
3805
|
+
const [operator, setOperator] = React14.useState("");
|
|
3806
|
+
const [value, setValue] = React14.useState("");
|
|
3807
|
+
const [value2, setValue2] = React14.useState("");
|
|
3430
3808
|
const current = fields.find((f) => f.field === field2) ?? fields[0];
|
|
3431
3809
|
const op = operator && current?.operators.includes(operator) ? operator : current?.operators[0] ?? "IN";
|
|
3432
3810
|
const valueless = op === "EMPTY" || op === "NOT_EMPTY";
|
|
@@ -3662,7 +4040,7 @@ function useKpiContents({
|
|
|
3662
4040
|
defaultIconColor = "#7b8499",
|
|
3663
4041
|
dateFormatter
|
|
3664
4042
|
}) {
|
|
3665
|
-
return
|
|
4043
|
+
return React14.useMemo(() => {
|
|
3666
4044
|
if (!contents?.length) return [];
|
|
3667
4045
|
const row = rows?.[0];
|
|
3668
4046
|
return contents.map((content, i) => {
|
|
@@ -3777,31 +4155,100 @@ function resolveDrillDown(config = {}) {
|
|
|
3777
4155
|
}
|
|
3778
4156
|
var WIDGET_TYPE = "KPI_CARD";
|
|
3779
4157
|
var KpiWidget = (props) => {
|
|
3780
|
-
const {
|
|
4158
|
+
const {
|
|
4159
|
+
title,
|
|
4160
|
+
description,
|
|
4161
|
+
emptyMessage,
|
|
4162
|
+
unconfiguredMessage,
|
|
4163
|
+
className,
|
|
4164
|
+
style,
|
|
4165
|
+
onItemClick,
|
|
4166
|
+
externalSeriesByIndex,
|
|
4167
|
+
...dataArgs
|
|
4168
|
+
} = props;
|
|
3781
4169
|
const ix = useInteractiveWidgetData({ ...dataArgs, widgetType: WIDGET_TYPE });
|
|
3782
4170
|
const { data, config, tokens: t } = ix;
|
|
3783
4171
|
const chrome = resolveChromeConfig(config);
|
|
3784
4172
|
const contents = config?.cardConfig?.contents ?? config?.contents ?? [];
|
|
3785
4173
|
const cardTemplate = config?.cardConfig?.template ?? config?.template ?? "standard";
|
|
3786
|
-
const
|
|
4174
|
+
const [fetchedSeries, setFetchedSeries] = React14.useState({});
|
|
4175
|
+
const onSeries = React14.useCallback((index, values) => {
|
|
4176
|
+
setFetchedSeries((prev) => {
|
|
4177
|
+
if (JSON.stringify(prev[index]) === JSON.stringify(values)) return prev;
|
|
4178
|
+
const next = { ...prev };
|
|
4179
|
+
if (values) next[index] = values;
|
|
4180
|
+
else delete next[index];
|
|
4181
|
+
return next;
|
|
4182
|
+
});
|
|
4183
|
+
}, []);
|
|
4184
|
+
const seriesByIndex = React14.useMemo(
|
|
4185
|
+
() => externalSeriesByIndex ? { ...fetchedSeries, ...externalSeriesByIndex } : fetchedSeries,
|
|
4186
|
+
[fetchedSeries, externalSeriesByIndex]
|
|
4187
|
+
);
|
|
4188
|
+
const vms = useKpiContents({ contents, rows: data.rows, cardTemplate, externalSeriesByIndex: seriesByIndex });
|
|
3787
4189
|
const state = data.state === "empty" && contents.length > 0 ? "ready" : data.state;
|
|
3788
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
4190
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4191
|
+
contents.map(
|
|
4192
|
+
(content, i) => content?.seriesSource?.mode === "timeseries" && content.seriesSource.dataSource && !externalSeriesByIndex?.[i] ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4193
|
+
KpiSeriesFetcher,
|
|
4194
|
+
{
|
|
4195
|
+
index: i,
|
|
4196
|
+
source: content.seriesSource,
|
|
4197
|
+
onSeries,
|
|
4198
|
+
dashFilters: dataArgs.dashFilters,
|
|
4199
|
+
cdateFilters: dataArgs.cdateFilters,
|
|
4200
|
+
localFilters: dataArgs.localFilters,
|
|
4201
|
+
workflowTypeId: dataArgs.workflowTypeId
|
|
4202
|
+
},
|
|
4203
|
+
i
|
|
4204
|
+
) : null
|
|
4205
|
+
),
|
|
4206
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4207
|
+
WidgetShell,
|
|
4208
|
+
{
|
|
4209
|
+
widgetType: WIDGET_TYPE,
|
|
4210
|
+
title: chrome.showHeader ? title ?? chrome.title : void 0,
|
|
4211
|
+
description: description ?? chrome.description,
|
|
4212
|
+
state,
|
|
4213
|
+
error: data.error,
|
|
4214
|
+
emptyMessage: emptyMessage ?? chrome.emptyMessage,
|
|
4215
|
+
unconfiguredMessage,
|
|
4216
|
+
className,
|
|
4217
|
+
style,
|
|
4218
|
+
actions: ix.toolbar,
|
|
4219
|
+
...ix.shell,
|
|
4220
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 14, height: "100%" }, children: vms.map((vm, i) => /* @__PURE__ */ jsxRuntime.jsx(KpiItem, { vm, index: i, onClick: onItemClick }, i)) })
|
|
4221
|
+
}
|
|
4222
|
+
)
|
|
4223
|
+
] });
|
|
4224
|
+
};
|
|
4225
|
+
var KpiSeriesFetcher = ({ index, source, onSeries, dashFilters, cdateFilters, localFilters, workflowTypeId }) => {
|
|
4226
|
+
const { dataSource, xField, valueField, dateGap } = source;
|
|
4227
|
+
const configurations = React14.useMemo(
|
|
4228
|
+
() => ({ series: [{ argumentField: xField, valueField }] }),
|
|
4229
|
+
[xField, valueField]
|
|
3804
4230
|
);
|
|
4231
|
+
const data = useWidgetData({
|
|
4232
|
+
widgetType: "KPI_SERIES",
|
|
4233
|
+
dataSource,
|
|
4234
|
+
configurations,
|
|
4235
|
+
dateGap: dateGap ?? null,
|
|
4236
|
+
dashFilters,
|
|
4237
|
+
cdateFilters,
|
|
4238
|
+
localFilters,
|
|
4239
|
+
workflowTypeId
|
|
4240
|
+
});
|
|
4241
|
+
const values = React14.useMemo(
|
|
4242
|
+
() => data.rows.map((r) => toNum(r?.[valueField])).filter((n) => n != null),
|
|
4243
|
+
[data.rows, valueField]
|
|
4244
|
+
);
|
|
4245
|
+
const ready = data.state === "ready" || data.state === "empty";
|
|
4246
|
+
const key = ready ? JSON.stringify(values) : "";
|
|
4247
|
+
React14.useEffect(() => {
|
|
4248
|
+
onSeries(index, ready ? values : void 0);
|
|
4249
|
+
}, [index, key, ready, onSeries]);
|
|
4250
|
+
React14.useEffect(() => () => onSeries(index, void 0), [index, onSeries]);
|
|
4251
|
+
return null;
|
|
3805
4252
|
};
|
|
3806
4253
|
var KpiItem = ({
|
|
3807
4254
|
vm,
|
|
@@ -3834,7 +4281,7 @@ var ChartWidget = (props) => {
|
|
|
3834
4281
|
} = props;
|
|
3835
4282
|
const ix = useInteractiveWidgetData({ ...dataArgs, widgetType });
|
|
3836
4283
|
const { data, config, tokens: t } = ix;
|
|
3837
|
-
const colorFor =
|
|
4284
|
+
const colorFor = React14.useMemo(() => {
|
|
3838
4285
|
const palette = t.colors.charts.palette;
|
|
3839
4286
|
return (index, s) => {
|
|
3840
4287
|
if (s?.color) return s.color;
|
|
@@ -3858,7 +4305,7 @@ var ChartWidget = (props) => {
|
|
|
3858
4305
|
const renderer = render ?? children;
|
|
3859
4306
|
let body;
|
|
3860
4307
|
if (renderer) body = renderer(args);
|
|
3861
|
-
else if (override) body =
|
|
4308
|
+
else if (override) body = React14__default.default.createElement(override, args);
|
|
3862
4309
|
else body = /* @__PURE__ */ jsxRuntime.jsx(FallbackBars, { ...args });
|
|
3863
4310
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3864
4311
|
WidgetShell,
|
|
@@ -3922,7 +4369,7 @@ var RankingBarWidget = (props) => {
|
|
|
3922
4369
|
const valueField = ranking.valueField ?? data.meta.series[0]?.valueField ?? "value";
|
|
3923
4370
|
const order = ranking.order === "asc" ? "asc" : "desc";
|
|
3924
4371
|
const limit = toNum(ranking.limit) ?? 10;
|
|
3925
|
-
const { rows, max } =
|
|
4372
|
+
const { rows, max } = React14.useMemo(() => {
|
|
3926
4373
|
const mapped = data.rows.map((r) => ({
|
|
3927
4374
|
label: r[labelField],
|
|
3928
4375
|
value: toNum(r[valueField]) ?? 0,
|
|
@@ -3968,7 +4415,7 @@ var RankingBarWidget = (props) => {
|
|
|
3968
4415
|
style,
|
|
3969
4416
|
actions: ix.toolbar,
|
|
3970
4417
|
...ix.shell,
|
|
3971
|
-
children: render ? render(args) : override ?
|
|
4418
|
+
children: render ? render(args) : override ? React14__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultRanking, { ...args })
|
|
3972
4419
|
}
|
|
3973
4420
|
);
|
|
3974
4421
|
};
|
|
@@ -4076,7 +4523,7 @@ var HeatMapWidget = (props) => {
|
|
|
4076
4523
|
medium: toNum(hm.thresholds?.medium?.value) ?? 60,
|
|
4077
4524
|
high: toNum(hm.thresholds?.high?.value) ?? 100
|
|
4078
4525
|
};
|
|
4079
|
-
const cells =
|
|
4526
|
+
const cells = React14.useMemo(() => data.rows.map((row) => {
|
|
4080
4527
|
const count = toNum(row[countField]) ?? 0;
|
|
4081
4528
|
const band = count <= thresholds.low ? "low" : count <= thresholds.medium ? "medium" : "high";
|
|
4082
4529
|
const configured = hm.thresholds?.[band]?.color;
|
|
@@ -4191,7 +4638,7 @@ function resolveCollectionMapping(config, series) {
|
|
|
4191
4638
|
return { ...fromSeries, ...block ?? config ?? {} };
|
|
4192
4639
|
}
|
|
4193
4640
|
function useCollectionItems(rows, mapping) {
|
|
4194
|
-
return
|
|
4641
|
+
return React14.useMemo(() => {
|
|
4195
4642
|
const m = mapping ?? {};
|
|
4196
4643
|
const detailFields = m.fields ?? m.dataConfs ?? [];
|
|
4197
4644
|
return rows.map((row, i) => {
|
|
@@ -4230,7 +4677,7 @@ var CollectionWidget = (props) => {
|
|
|
4230
4677
|
const ix = useInteractiveWidgetData({ ...dataArgs, widgetType });
|
|
4231
4678
|
const { data, config, tokens: t } = ix;
|
|
4232
4679
|
const chrome = resolveChromeConfig(config);
|
|
4233
|
-
const mapping =
|
|
4680
|
+
const mapping = React14.useMemo(() => resolveCollectionMapping(config, data.meta.series), [config, data.meta.series]);
|
|
4234
4681
|
const items = useCollectionItems(data.rows, mapping);
|
|
4235
4682
|
const template = config?.template || "standard";
|
|
4236
4683
|
const override = useTemplateOverride(widgetType, template);
|
|
@@ -4256,7 +4703,7 @@ var CollectionWidget = (props) => {
|
|
|
4256
4703
|
style,
|
|
4257
4704
|
actions: ix.toolbar,
|
|
4258
4705
|
...ix.shell,
|
|
4259
|
-
children: render ? render(args) : override ?
|
|
4706
|
+
children: render ? render(args) : override ? React14__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultList, { ...args })
|
|
4260
4707
|
}
|
|
4261
4708
|
);
|
|
4262
4709
|
};
|
|
@@ -4325,7 +4772,7 @@ var KIND_BY_TYPE = {
|
|
|
4325
4772
|
RICHTEXT: "html"
|
|
4326
4773
|
};
|
|
4327
4774
|
function useTableColumns(explicit, config, rows) {
|
|
4328
|
-
return
|
|
4775
|
+
return React14.useMemo(() => {
|
|
4329
4776
|
if (explicit?.length) return explicit;
|
|
4330
4777
|
const declared = [config?.columns, config?.additionalProperties?.columns, config?.fields, config?.table?.columns].find((d) => Array.isArray(d) && d.length);
|
|
4331
4778
|
if (Array.isArray(declared) && declared.length) {
|
|
@@ -4506,11 +4953,11 @@ var PivotGridWidget = (props) => {
|
|
|
4506
4953
|
};
|
|
4507
4954
|
function toDate(v) {
|
|
4508
4955
|
if (v === null || v === void 0 || v === "") return null;
|
|
4509
|
-
const d =
|
|
4956
|
+
const d = dayjs4__default.default(v);
|
|
4510
4957
|
return d.isValid() ? d.toDate() : null;
|
|
4511
4958
|
}
|
|
4512
4959
|
function useTemporalEvents(rows, mapping) {
|
|
4513
|
-
return
|
|
4960
|
+
return React14.useMemo(() => {
|
|
4514
4961
|
const m = mapping ?? {};
|
|
4515
4962
|
const titleField = m.taskTitle ?? m.titleField ?? m.title ?? m.labelField;
|
|
4516
4963
|
const groupField = m.projectTitle ?? m.groupField ?? m.group ?? m.resourceField;
|
|
@@ -4531,7 +4978,7 @@ function useTemporalEvents(rows, mapping) {
|
|
|
4531
4978
|
start,
|
|
4532
4979
|
end,
|
|
4533
4980
|
// With no time component on either bound, treat it as an all-day span.
|
|
4534
|
-
allDay: !!(start && !end) || !!start &&
|
|
4981
|
+
allDay: !!(start && !end) || !!start && dayjs4__default.default(start).startOf("day").isSame(start),
|
|
4535
4982
|
status: statusRaw !== void 0 ? { label: statusRaw, tone: statusTone(statusRaw) } : void 0,
|
|
4536
4983
|
progress: Number.isFinite(rawProgress) ? Math.max(0, Math.min(1, rawProgress > 1 ? rawProgress / 100 : rawProgress)) : void 0,
|
|
4537
4984
|
color: colorField ? row?.[colorField] : void 0,
|
|
@@ -4558,12 +5005,12 @@ var TemporalWidget = (props) => {
|
|
|
4558
5005
|
const chrome = resolveChromeConfig(config);
|
|
4559
5006
|
const mapping = config?.timeline ?? config?.gantt ?? config?.calendar ?? config?.scheduler ?? config?.mapping ?? config;
|
|
4560
5007
|
const events = useTemporalEvents(data.rows, mapping);
|
|
4561
|
-
const range =
|
|
5008
|
+
const range = React14.useMemo(() => {
|
|
4562
5009
|
const stamps = events.flatMap((e) => [e.start, e.end].filter(Boolean).map((d) => d.getTime()));
|
|
4563
5010
|
if (!stamps.length) return null;
|
|
4564
5011
|
return { start: new Date(Math.min(...stamps)), end: new Date(Math.max(...stamps)) };
|
|
4565
5012
|
}, [events]);
|
|
4566
|
-
const lanes =
|
|
5013
|
+
const lanes = React14.useMemo(() => {
|
|
4567
5014
|
const map = /* @__PURE__ */ new Map();
|
|
4568
5015
|
events.forEach((e) => {
|
|
4569
5016
|
const key = String(e.group ?? "");
|
|
@@ -4598,7 +5045,7 @@ var TemporalWidget = (props) => {
|
|
|
4598
5045
|
style,
|
|
4599
5046
|
actions: ix.toolbar,
|
|
4600
5047
|
...ix.shell,
|
|
4601
|
-
children: render ? render(args) : override ?
|
|
5048
|
+
children: render ? render(args) : override ? React14__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultTimeline, { ...args })
|
|
4602
5049
|
}
|
|
4603
5050
|
);
|
|
4604
5051
|
};
|
|
@@ -4625,8 +5072,8 @@ var DefaultTimeline = ({ events, tokens: t, onEventClick }) => /* @__PURE__ */ j
|
|
|
4625
5072
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
4626
5073
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: t.typography.body.fontSize, color: t.colors.textPrimary, fontWeight: 600 }, children: String(e.title ?? "") }),
|
|
4627
5074
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: t.typography.caption.fontSize, color: t.colors.textTertiary }, children: [
|
|
4628
|
-
e.start ?
|
|
4629
|
-
e.end ? ` \u2192 ${
|
|
5075
|
+
e.start ? dayjs4__default.default(e.start).format("DD MMM YYYY") : "",
|
|
5076
|
+
e.end ? ` \u2192 ${dayjs4__default.default(e.end).format("DD MMM YYYY")}` : "",
|
|
4630
5077
|
e.group ? ` \xB7 ${String(e.group)}` : ""
|
|
4631
5078
|
] })
|
|
4632
5079
|
] }),
|
|
@@ -4657,7 +5104,7 @@ var StepperWidget = (props) => {
|
|
|
4657
5104
|
const descField = stepper.descriptionField;
|
|
4658
5105
|
const statusField = stepper.statusField ?? "status";
|
|
4659
5106
|
const authored = Array.isArray(stepper.steps) && stepper.steps.length > 0;
|
|
4660
|
-
const steps =
|
|
5107
|
+
const steps = React14.useMemo(() => {
|
|
4661
5108
|
if (authored) {
|
|
4662
5109
|
const live = stepper.statusField ? String(data.rows[0]?.[stepper.statusField] ?? "").trim().toLowerCase() : "";
|
|
4663
5110
|
const liveIdx = live ? stepper.steps.findIndex((s) => String(s?.title ?? s?.label ?? "").trim().toLowerCase() === live) : -1;
|
|
@@ -4721,7 +5168,7 @@ var StepperWidget = (props) => {
|
|
|
4721
5168
|
style,
|
|
4722
5169
|
actions: ix.toolbar,
|
|
4723
5170
|
...ix.shell,
|
|
4724
|
-
children: render ? render(args) : override ?
|
|
5171
|
+
children: render ? render(args) : override ? React14__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultStepper, { ...args })
|
|
4725
5172
|
}
|
|
4726
5173
|
);
|
|
4727
5174
|
};
|
|
@@ -4793,7 +5240,7 @@ function renderHtmlVariables(template, rows, variables) {
|
|
|
4793
5240
|
} else if (v.dataFormat === "AMOUNT" && typeof value === "number") {
|
|
4794
5241
|
value = formatAmountValue(value, v.amountSuffix || "Rupees", v.precision ?? 2);
|
|
4795
5242
|
} else if (v.dataFormat === "DATE" && value) {
|
|
4796
|
-
const d =
|
|
5243
|
+
const d = dayjs4__default.default(value);
|
|
4797
5244
|
if (d.isValid()) value = d.format(DATE_FORMAT_MAP[v.dateFormat] || "DD/MM/YYYY");
|
|
4798
5245
|
}
|
|
4799
5246
|
let text2;
|
|
@@ -4826,7 +5273,7 @@ var HtmlWidget = (props) => {
|
|
|
4826
5273
|
const chrome = resolveChromeConfig(config);
|
|
4827
5274
|
const template = config?.media?.htmlText ?? config?.html ?? config?.content ?? config?.rawHtml ?? "";
|
|
4828
5275
|
const variables = Array.isArray(config?.variables) ? config.variables : [];
|
|
4829
|
-
const html =
|
|
5276
|
+
const html = React14.useMemo(
|
|
4830
5277
|
() => variables.length ? renderHtmlVariables(template, data.rows, variables) : interpolate(template, data.rows[0]),
|
|
4831
5278
|
[template, data.rows, variables]
|
|
4832
5279
|
);
|
|
@@ -4863,7 +5310,7 @@ var IframeWidget = (props) => {
|
|
|
4863
5310
|
const { data, config, tokens: t } = ix;
|
|
4864
5311
|
const chrome = resolveChromeConfig(config);
|
|
4865
5312
|
const raw2 = config?.media?.src ?? config?.url ?? config?.src ?? config?.iframe?.url ?? "";
|
|
4866
|
-
const url =
|
|
5313
|
+
const url = React14.useMemo(() => interpolate(raw2, data.rows[0]), [raw2, data.rows]);
|
|
4867
5314
|
const state = url ? "ready" : data.state === "loading" ? "loading" : "unconfigured";
|
|
4868
5315
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4869
5316
|
WidgetShell,
|
|
@@ -4903,14 +5350,14 @@ var GoogleMapWidget = (props) => {
|
|
|
4903
5350
|
const labelField = m.labelField ?? m.titleField ?? m.pinDataTitle;
|
|
4904
5351
|
const centerLat = gm?.center?.latitude ?? m.centerLat;
|
|
4905
5352
|
const centerLng = gm?.center?.longitude ?? m.centerLng;
|
|
4906
|
-
const markers =
|
|
5353
|
+
const markers = React14.useMemo(() => data.rows.flatMap((row, i) => {
|
|
4907
5354
|
const lat = Number(row?.[latField]);
|
|
4908
5355
|
const lng = Number(row?.[lngField]);
|
|
4909
5356
|
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return [];
|
|
4910
5357
|
const label = titleParts.length ? titleParts.map((p, k) => `${k ? p.separator ?? " " : ""}${row?.[p.field] ?? ""}`).join("").trim() : labelField ? row?.[labelField] : void 0;
|
|
4911
5358
|
return [{ id: row?.id ?? i, lat, lng, label, raw: row }];
|
|
4912
5359
|
}), [data.rows, latField, lngField, labelField, titleParts]);
|
|
4913
|
-
const center =
|
|
5360
|
+
const center = React14.useMemo(() => {
|
|
4914
5361
|
if (centerLat != null && centerLng != null && Number.isFinite(Number(centerLat))) {
|
|
4915
5362
|
return { lat: Number(centerLat), lng: Number(centerLng) };
|
|
4916
5363
|
}
|
|
@@ -4957,7 +5404,7 @@ var ActionCardWidget = (props) => {
|
|
|
4957
5404
|
const ix = useInteractiveWidgetData({ ...dataArgs, widgetType: "ACTION_CARD" });
|
|
4958
5405
|
const { data, config, tokens: t } = ix;
|
|
4959
5406
|
const chrome = resolveChromeConfig(config);
|
|
4960
|
-
const actions =
|
|
5407
|
+
const actions = React14.useMemo(() => {
|
|
4961
5408
|
const declared = config?.actions ?? config?.buttons ?? (config?.action ? [{ ...config.action, actionType: config.actionType, label: config.action.label ?? config.action.FormTitle }] : []);
|
|
4962
5409
|
return declared.map((a, i) => ({
|
|
4963
5410
|
id: a?.id ?? a?.FormID ?? i,
|
|
@@ -5038,7 +5485,7 @@ var TreeWidget = (props) => {
|
|
|
5038
5485
|
const { data, config, tokens: t } = ix;
|
|
5039
5486
|
const chrome = resolveChromeConfig(config);
|
|
5040
5487
|
const tree = config?.treeConfiguration ?? config?.tree ?? config ?? {};
|
|
5041
|
-
const nodes =
|
|
5488
|
+
const nodes = React14.useMemo(() => buildTree(
|
|
5042
5489
|
data.rows,
|
|
5043
5490
|
tree.idField ?? "id",
|
|
5044
5491
|
tree.parentField ?? "parentId",
|
|
@@ -5873,7 +6320,7 @@ function dynamicDefault(el2, user) {
|
|
|
5873
6320
|
const t = resolveFieldType(el2);
|
|
5874
6321
|
if ((dd.operator === "ADD" || dd.operator === "SUB") && (t === "DATE_PICKER" || t === "DATE_TIME_PICKER")) {
|
|
5875
6322
|
const sign = dd.operator === "SUB" ? -1 : 1;
|
|
5876
|
-
let d =
|
|
6323
|
+
let d = dayjs4__default.default();
|
|
5877
6324
|
const u = dd.units;
|
|
5878
6325
|
if (typeof u === "number" || typeof u === "string" && u.trim() !== "" && !isNaN(Number(u))) d = d.add(sign * Number(u), "day");
|
|
5879
6326
|
else Object.entries(u ?? {}).forEach(([k, n]) => {
|
|
@@ -5913,7 +6360,7 @@ function buildInitialValues(elements, opts = {}) {
|
|
|
5913
6360
|
}
|
|
5914
6361
|
function formatDateish(t, value) {
|
|
5915
6362
|
const isDayjs = (v) => v && typeof v === "object" && typeof v.isValid === "function" && typeof v.format === "function";
|
|
5916
|
-
const toD = (v) => isDayjs(v) ? v : v instanceof Date ?
|
|
6363
|
+
const toD = (v) => isDayjs(v) ? v : v instanceof Date ? dayjs4__default.default(v) : typeof v === "string" && v ? dayjs4__default.default(v) : null;
|
|
5917
6364
|
if (t === "DATE_PICKER") {
|
|
5918
6365
|
const d = toD(value);
|
|
5919
6366
|
return d?.isValid() ? d.format("YYYY-MM-DD") : value;
|
|
@@ -6028,9 +6475,9 @@ function formatFieldValue(el2, value) {
|
|
|
6028
6475
|
const t = resolveFieldType(el2);
|
|
6029
6476
|
if (t === "CHECK_BOX") return value ? "Yes" : "No";
|
|
6030
6477
|
if (RELATIONAL_TYPES.has(t)) return (Array.isArray(value) ? value : [value]).map((v) => v?.view ?? v?.select ?? String(v)).join(", ");
|
|
6031
|
-
if (t === "INPUT_DATE_RANGE" && Array.isArray(value)) return value.filter(Boolean).map((v) =>
|
|
6032
|
-
if (t === "DATE_PICKER") return
|
|
6033
|
-
if (t === "DATE_TIME_PICKER") return
|
|
6478
|
+
if (t === "INPUT_DATE_RANGE" && Array.isArray(value)) return value.filter(Boolean).map((v) => dayjs4__default.default(v).format("D MMM YYYY")).join(" \u2013 ");
|
|
6479
|
+
if (t === "DATE_PICKER") return dayjs4__default.default(value).isValid() ? dayjs4__default.default(value).format("D MMM YYYY") : String(value);
|
|
6480
|
+
if (t === "DATE_TIME_PICKER") return dayjs4__default.default(value).isValid() ? dayjs4__default.default(value).format("D MMM YYYY, HH:mm") : String(value);
|
|
6034
6481
|
if (t === "INPUT_PERCENT") return `${value}%`;
|
|
6035
6482
|
if (Array.isArray(value)) return value.map((v) => v?.media_name ?? v?.view ?? String(v)).join(", ");
|
|
6036
6483
|
return String(value);
|
|
@@ -6494,28 +6941,28 @@ var isFilled = (v) => v != null && v !== "" && !(Array.isArray(v) && !v.length);
|
|
|
6494
6941
|
function useFieldOptions(form, field2, opts = {}) {
|
|
6495
6942
|
const { debounceMs = 250, loadOn = "focus", autofillExtraLabels = false } = opts;
|
|
6496
6943
|
const el2 = field2.element;
|
|
6497
|
-
const cfg =
|
|
6944
|
+
const cfg = React14.useMemo(() => field2.kind === "lookup" ? lookupConfigOf(el2) : null, [field2.kind, el2]);
|
|
6498
6945
|
const people = field2.kind === "user" && !!(el2.properties?.jsonData || el2.properties?.searchType === "GENERIC_FORM_AUTO_SEARCH");
|
|
6499
6946
|
const elements = form.definition?.elements ?? [];
|
|
6500
|
-
const rules =
|
|
6501
|
-
const dep =
|
|
6947
|
+
const rules = React14.useMemo(() => optionFilterRules(elements, field2.id), [elements, field2.id]);
|
|
6948
|
+
const dep = React14.useMemo(() => cfg ? lookupDependencyFilters(cfg, rules, elements, form.values) : { filters: [], satisfied: true }, [cfg, rules, elements, form.values]);
|
|
6502
6949
|
const depKey = JSON.stringify(dep.filters);
|
|
6503
6950
|
const maxCount = cfg ? cfg.maxResults : field2.kind === "user" ? maxResultsOf(el2) : field2.multiple ? Infinity : 1;
|
|
6504
6951
|
const single = maxCount === 1;
|
|
6505
|
-
const [search, setSearch] =
|
|
6506
|
-
const [q, setQ] =
|
|
6507
|
-
|
|
6952
|
+
const [search, setSearch] = React14.useState("");
|
|
6953
|
+
const [q, setQ] = React14.useState("");
|
|
6954
|
+
React14.useEffect(() => {
|
|
6508
6955
|
const t = setTimeout(() => setQ(search), debounceMs);
|
|
6509
6956
|
return () => clearTimeout(t);
|
|
6510
6957
|
}, [search, debounceMs]);
|
|
6511
|
-
const [remote, setRemote] =
|
|
6512
|
-
const [loading, setLoading] =
|
|
6513
|
-
const [error, setError] =
|
|
6514
|
-
const [opened, setOpened] =
|
|
6515
|
-
const [nonce, setNonce] =
|
|
6516
|
-
const lastFetch =
|
|
6517
|
-
const maxAge =
|
|
6518
|
-
const waitingFor =
|
|
6958
|
+
const [remote, setRemote] = React14.useState([]);
|
|
6959
|
+
const [loading, setLoading] = React14.useState(false);
|
|
6960
|
+
const [error, setError] = React14.useState(null);
|
|
6961
|
+
const [opened, setOpened] = React14.useState(loadOn === "mount");
|
|
6962
|
+
const [nonce, setNonce] = React14.useState(0);
|
|
6963
|
+
const lastFetch = React14.useRef(0);
|
|
6964
|
+
const maxAge = React14.useRef(1e4);
|
|
6965
|
+
const waitingFor = React14.useMemo(() => {
|
|
6519
6966
|
if (!cfg || dep.satisfied || cfg.operator !== "$and") return null;
|
|
6520
6967
|
const missing = rules.find((r) => !isFilled(form.values[r.elementId]));
|
|
6521
6968
|
const lbl = missing ? form.field(missing.elementId)?.label : cfg.dynamicFilters[0]?.DependencyFieldLabel;
|
|
@@ -6523,7 +6970,7 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6523
6970
|
}, [cfg, dep.satisfied, rules, form]);
|
|
6524
6971
|
const depDriven = !!cfg && rules.length > 0 && dep.filters.length > 0;
|
|
6525
6972
|
const active = opened || depDriven || q !== "";
|
|
6526
|
-
|
|
6973
|
+
React14.useEffect(() => {
|
|
6527
6974
|
if (!cfg && !people) return;
|
|
6528
6975
|
if (cfg && (waitingFor || cfg.loadOnDependency && !dep.filters.length && rules.length)) {
|
|
6529
6976
|
setRemote([]);
|
|
@@ -6549,12 +6996,12 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6549
6996
|
alive = false;
|
|
6550
6997
|
};
|
|
6551
6998
|
}, [cfg, people, depKey, q, waitingFor, form.client, active, nonce]);
|
|
6552
|
-
const refresh =
|
|
6999
|
+
const refresh = React14.useCallback(() => {
|
|
6553
7000
|
maxAge.current = 0;
|
|
6554
7001
|
setOpened(true);
|
|
6555
7002
|
setNonce((n) => n + 1);
|
|
6556
7003
|
}, []);
|
|
6557
|
-
const onFocus =
|
|
7004
|
+
const onFocus = React14.useCallback(() => {
|
|
6558
7005
|
if (!cfg && !people) return;
|
|
6559
7006
|
if (!opened) {
|
|
6560
7007
|
setOpened(true);
|
|
@@ -6565,8 +7012,8 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6565
7012
|
setNonce((n) => n + 1);
|
|
6566
7013
|
} else if (people && !remote.length) setNonce((n) => n + 1);
|
|
6567
7014
|
}, [cfg, people, opened, remote.length]);
|
|
6568
|
-
const prevDepKey =
|
|
6569
|
-
|
|
7015
|
+
const prevDepKey = React14.useRef(null);
|
|
7016
|
+
React14.useEffect(() => {
|
|
6570
7017
|
if (!cfg || !rules.length) return;
|
|
6571
7018
|
if (prevDepKey.current === null) {
|
|
6572
7019
|
prevDepKey.current = depKey;
|
|
@@ -6577,14 +7024,14 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6577
7024
|
if (form.readOnly) return;
|
|
6578
7025
|
if (isFilled(form.values[field2.id])) form.setValue(field2.id, single ? null : []);
|
|
6579
7026
|
}, [depKey]);
|
|
6580
|
-
const staticFiltered =
|
|
7027
|
+
const staticFiltered = React14.useMemo(() => {
|
|
6581
7028
|
if (cfg || people) return remote;
|
|
6582
7029
|
const needle = search.trim().toLowerCase();
|
|
6583
7030
|
return needle ? field2.options.filter((o) => o.label.toLowerCase().includes(needle)) : field2.options;
|
|
6584
7031
|
}, [cfg, people, remote, field2.options, search]);
|
|
6585
|
-
const pickedCache =
|
|
7032
|
+
const pickedCache = React14.useRef(/* @__PURE__ */ new Map());
|
|
6586
7033
|
const current = field2.value;
|
|
6587
|
-
const selected =
|
|
7034
|
+
const selected = React14.useMemo(() => {
|
|
6588
7035
|
const arr = current == null || current === "" ? [] : Array.isArray(current) ? current : [current];
|
|
6589
7036
|
const pool = [...staticFiltered, ...field2.options];
|
|
6590
7037
|
return arr.map((v) => {
|
|
@@ -6594,7 +7041,7 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6594
7041
|
return { label: String(v), value: v };
|
|
6595
7042
|
});
|
|
6596
7043
|
}, [current, staticFiltered, field2.options]);
|
|
6597
|
-
const store =
|
|
7044
|
+
const store = React14.useCallback((picked) => {
|
|
6598
7045
|
picked.forEach((o) => pickedCache.current.set(optionKey(o.value), o));
|
|
6599
7046
|
const t = field2.type;
|
|
6600
7047
|
if (field2.kind === "lookup") {
|
|
@@ -6614,24 +7061,24 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6614
7061
|
if (field2.multiple) form.setValue(field2.id, picked.slice(0, maxCount).map((o) => o.value));
|
|
6615
7062
|
else form.setValue(field2.id, picked[0]?.value ?? (t === "CHECK_BOX" ? false : ""));
|
|
6616
7063
|
}, [field2, form, cfg, single, maxCount, autofillExtraLabels]);
|
|
6617
|
-
const autoPickRef =
|
|
7064
|
+
const autoPickRef = React14.useRef(() => {
|
|
6618
7065
|
});
|
|
6619
|
-
const lastAutoKey =
|
|
7066
|
+
const lastAutoKey = React14.useRef(null);
|
|
6620
7067
|
autoPickRef.current = (o, key) => {
|
|
6621
7068
|
if (form.readOnly || lastAutoKey.current === key) return;
|
|
6622
7069
|
lastAutoKey.current = key;
|
|
6623
7070
|
if (isFilled(form.values[field2.id])) return;
|
|
6624
7071
|
store([o]);
|
|
6625
7072
|
};
|
|
6626
|
-
const select =
|
|
6627
|
-
const isSelected =
|
|
7073
|
+
const select = React14.useCallback((o) => store(o == null ? [] : Array.isArray(o) ? o : [o]), [store]);
|
|
7074
|
+
const isSelected = React14.useCallback((o) => selected.some((s) => optionKey(s.value) === optionKey(o.value)), [selected]);
|
|
6628
7075
|
const atLimit = field2.multiple && selected.length >= maxCount;
|
|
6629
|
-
const toggle =
|
|
7076
|
+
const toggle = React14.useCallback((o) => {
|
|
6630
7077
|
const on = isSelected(o);
|
|
6631
7078
|
if (!on && field2.multiple && selected.length >= maxCount) return;
|
|
6632
7079
|
store(on ? selected.filter((s) => optionKey(s.value) !== optionKey(o.value)) : field2.multiple ? [...selected, o] : [o]);
|
|
6633
7080
|
}, [isSelected, selected, store, field2.multiple, maxCount]);
|
|
6634
|
-
const addFreeTag =
|
|
7081
|
+
const addFreeTag = React14.useCallback((text2) => {
|
|
6635
7082
|
const t = text2.trim();
|
|
6636
7083
|
if (!t || field2.type !== "INPUT_TAG") return;
|
|
6637
7084
|
const o = { label: t, value: t, extra: [], raw: { view: t, select: t, extraLabels: [] } };
|
|
@@ -6662,9 +7109,9 @@ function useFieldOptions(form, field2, opts = {}) {
|
|
|
6662
7109
|
var BLOCKED_FILE_EXTENSIONS = new Set("ade adp app application appref-ms asp aspx asx bas bat bgi cab cer chm cmd cnt com cpl crt csh der diagcab exe fxp gadget grp hlp hpj hta htc inf ins iso isp its jar jnlp js jse ksh lnk mad maf mag mam maq mar mas mat mau mav maw mcf mda mdb mde mdt mdw mdz msc msh msh1 msh2 mshxml msh1xml msh2xml msi msp mst msu ops osd pcd pif pl plg prf ps1 ps1xml ps2 ps2xml psc1 psc2 reg scr scf sln vbs vb vbe ws wsf wsc wsh z bz2 ace ppkg bak tmp ost pst pkg vhd vhdx lock lck cs csproj resx config resources pdb manifest dot wbk xlt xlm xla pot pps cdb mdn sh php mdf py ldb wps xlsb xlam xll xlw ppam".split(" "));
|
|
6663
7110
|
function useFileField(form, field2, opts = {}) {
|
|
6664
7111
|
const stored = Array.isArray(field2.value) ? field2.value : [];
|
|
6665
|
-
const [pending2, setPending] =
|
|
6666
|
-
const [reject, setReject] =
|
|
6667
|
-
const storedRef =
|
|
7112
|
+
const [pending2, setPending] = React14.useState([]);
|
|
7113
|
+
const [reject, setReject] = React14.useState(null);
|
|
7114
|
+
const storedRef = React14.useRef(stored);
|
|
6668
7115
|
storedRef.current = stored;
|
|
6669
7116
|
const items = [
|
|
6670
7117
|
...stored.map((r, i) => ({
|
|
@@ -6679,7 +7126,7 @@ function useFileField(form, field2, opts = {}) {
|
|
|
6679
7126
|
...pending2
|
|
6680
7127
|
];
|
|
6681
7128
|
const max = field2.maxCount;
|
|
6682
|
-
const add =
|
|
7129
|
+
const add = React14.useCallback((list) => {
|
|
6683
7130
|
setReject(null);
|
|
6684
7131
|
const files = Array.from(list);
|
|
6685
7132
|
const room = max ? Math.max(0, max - storedRef.current.length - pending2.filter((p) => p.uploading).length) : files.length;
|
|
@@ -6713,7 +7160,7 @@ function useFileField(form, field2, opts = {}) {
|
|
|
6713
7160
|
});
|
|
6714
7161
|
if (files.length > room) setReject(`Only ${room} more file${room === 1 ? "" : "s"} can be attached`);
|
|
6715
7162
|
}, [form, field2, max, pending2, opts.zappId]);
|
|
6716
|
-
const remove =
|
|
7163
|
+
const remove = React14.useCallback((key) => {
|
|
6717
7164
|
if (key.startsWith("p")) {
|
|
6718
7165
|
setPending((p) => p.filter((x) => x.key !== key));
|
|
6719
7166
|
return;
|
|
@@ -6733,10 +7180,10 @@ function sheetAllows(p, action) {
|
|
|
6733
7180
|
}
|
|
6734
7181
|
function useSheetField(form, field2) {
|
|
6735
7182
|
const childId = String(field2.element.properties?.formID ?? field2.element.properties?.FormID ?? field2.element.properties?.nestedFormId ?? field2.element.properties?.dataSource?.FormID ?? "");
|
|
6736
|
-
const [child, setChild] =
|
|
6737
|
-
const [loading, setLoading] =
|
|
6738
|
-
const [error, setError] =
|
|
6739
|
-
|
|
7183
|
+
const [child, setChild] = React14.useState(null);
|
|
7184
|
+
const [loading, setLoading] = React14.useState(!!childId);
|
|
7185
|
+
const [error, setError] = React14.useState(childId ? null : "No child form configured");
|
|
7186
|
+
React14.useEffect(() => {
|
|
6740
7187
|
if (!childId) return;
|
|
6741
7188
|
let alive = true;
|
|
6742
7189
|
setLoading(true);
|
|
@@ -6755,7 +7202,7 @@ function useSheetField(form, field2) {
|
|
|
6755
7202
|
alive = false;
|
|
6756
7203
|
};
|
|
6757
7204
|
}, [childId, form.client]);
|
|
6758
|
-
const columns =
|
|
7205
|
+
const columns = React14.useMemo(() => (child?.elements ?? []).filter((e) => !e.hidden && !["COLLAPSE_SECTION", "CONTAINER", "GROUP_VIEW", "TEXT_VIEW", "ACTION_BUTTON"].includes(resolveFieldType(e))).map((e) => ({
|
|
6759
7206
|
id: e.elementId,
|
|
6760
7207
|
label: e.label,
|
|
6761
7208
|
type: resolveFieldType(e),
|
|
@@ -6766,10 +7213,10 @@ function useSheetField(form, field2) {
|
|
|
6766
7213
|
const rows = (Array.isArray(field2.value) ? field2.value : []).filter((r) => r && typeof r === "object");
|
|
6767
7214
|
const p = field2.element.properties ?? {};
|
|
6768
7215
|
const maxRows = Number(p.maxRows) > 0 ? Number(p.maxRows) : void 0;
|
|
6769
|
-
const [limitMessage, setLimitMessage] =
|
|
7216
|
+
const [limitMessage, setLimitMessage] = React14.useState(null);
|
|
6770
7217
|
const addAllowed = !field2.disabled && sheetAllows(p, "ADD");
|
|
6771
7218
|
const canDelete = !field2.disabled && sheetAllows(p, "DELETE");
|
|
6772
|
-
const addRow =
|
|
7219
|
+
const addRow = React14.useCallback((vals) => {
|
|
6773
7220
|
if (!addAllowed) return;
|
|
6774
7221
|
if (maxRows && rows.length >= maxRows) {
|
|
6775
7222
|
setLimitMessage(`Maximum number of rows reached (${maxRows})`);
|
|
@@ -6779,16 +7226,16 @@ function useSheetField(form, field2) {
|
|
|
6779
7226
|
const blank = child ? computeRowFormulas(child.elements, buildInitialValues(child.elements, { defaults: vals })) : { ...vals ?? {} };
|
|
6780
7227
|
form.setValue(field2.id, [...rows, { ...blank, _rowKey: `r${Date.now()}${Math.random()}` }]);
|
|
6781
7228
|
}, [child, rows, form, field2.id, addAllowed, maxRows]);
|
|
6782
|
-
const updateRow =
|
|
7229
|
+
const updateRow = React14.useCallback((i, col, v) => {
|
|
6783
7230
|
const recalc = (r) => child ? computeRowFormulas(child.elements, r) : r;
|
|
6784
7231
|
form.setValue(field2.id, rows.map((r, j) => j === i ? recalc({ ...r, [col]: v, _dirty: true }) : r));
|
|
6785
7232
|
}, [rows, form, field2.id, child]);
|
|
6786
|
-
const removeRow =
|
|
7233
|
+
const removeRow = React14.useCallback((i) => {
|
|
6787
7234
|
if (!canDelete) return;
|
|
6788
7235
|
setLimitMessage(null);
|
|
6789
7236
|
form.setValue(field2.id, rows.filter((_2, j) => j !== i));
|
|
6790
7237
|
}, [rows, form, field2.id, canDelete]);
|
|
6791
|
-
const rowErrors =
|
|
7238
|
+
const rowErrors = React14.useMemo(() => {
|
|
6792
7239
|
const out = {};
|
|
6793
7240
|
if (!form.submitAttempted) return out;
|
|
6794
7241
|
rows.forEach((r, i) => {
|
|
@@ -6848,9 +7295,9 @@ function acceptOf(el2) {
|
|
|
6848
7295
|
if (!list.length) return el2.type === "UPLOAD_FILE_IMAGE" ? "image/*" : void 0;
|
|
6849
7296
|
return list.map((s) => s.includes("/") || s.startsWith(".") ? s : `.${s}`).join(",");
|
|
6850
7297
|
}
|
|
6851
|
-
var FormApiContext =
|
|
7298
|
+
var FormApiContext = React14__default.default.createContext(null);
|
|
6852
7299
|
function useFormApi() {
|
|
6853
|
-
const f =
|
|
7300
|
+
const f = React14__default.default.useContext(FormApiContext);
|
|
6854
7301
|
if (!f) throw new Error("@zestl/zvolv-widgets: useFormApi must be used inside <ZvolvForm> or FormApiContext");
|
|
6855
7302
|
return f;
|
|
6856
7303
|
}
|
|
@@ -6859,34 +7306,34 @@ function useZvolvForm(args) {
|
|
|
6859
7306
|
const mode = args.mode ?? (submissionId ? "EDIT" : "NEW");
|
|
6860
7307
|
const rt = useZvolvRuntime();
|
|
6861
7308
|
const client = rt.client;
|
|
6862
|
-
const argsRef =
|
|
7309
|
+
const argsRef = React14.useRef(args);
|
|
6863
7310
|
argsRef.current = args;
|
|
6864
|
-
const [definition, setDefinition] =
|
|
6865
|
-
const [status, setStatus] =
|
|
6866
|
-
const [error, setError] =
|
|
6867
|
-
const [values, setValues] =
|
|
6868
|
-
const [serverErrors, setServerErrors] =
|
|
6869
|
-
const [touched, setTouched] =
|
|
6870
|
-
const [submitAttempted, setSubmitAttempted] =
|
|
6871
|
-
const [flagged, setFlagged] =
|
|
6872
|
-
const [submitting, setSubmitting] =
|
|
6873
|
-
const [submitError, setSubmitError] =
|
|
6874
|
-
const [submittedId, setSubmittedId] =
|
|
6875
|
-
const [dirty, setDirty] =
|
|
6876
|
-
const [busy, setBusy] =
|
|
6877
|
-
const initialRef =
|
|
6878
|
-
const valuesRef =
|
|
7311
|
+
const [definition, setDefinition] = React14.useState(null);
|
|
7312
|
+
const [status, setStatus] = React14.useState("loading");
|
|
7313
|
+
const [error, setError] = React14.useState(null);
|
|
7314
|
+
const [values, setValues] = React14.useState({});
|
|
7315
|
+
const [serverErrors, setServerErrors] = React14.useState({});
|
|
7316
|
+
const [touched, setTouched] = React14.useState(/* @__PURE__ */ new Set());
|
|
7317
|
+
const [submitAttempted, setSubmitAttempted] = React14.useState(false);
|
|
7318
|
+
const [flagged, setFlagged] = React14.useState(/* @__PURE__ */ new Set());
|
|
7319
|
+
const [submitting, setSubmitting] = React14.useState(false);
|
|
7320
|
+
const [submitError, setSubmitError] = React14.useState(null);
|
|
7321
|
+
const [submittedId, setSubmittedId] = React14.useState(null);
|
|
7322
|
+
const [dirty, setDirty] = React14.useState(false);
|
|
7323
|
+
const [busy, setBusy] = React14.useState(/* @__PURE__ */ new Set());
|
|
7324
|
+
const initialRef = React14.useRef({});
|
|
7325
|
+
const valuesRef = React14.useRef({});
|
|
6879
7326
|
valuesRef.current = values;
|
|
6880
|
-
const sheetDefs =
|
|
6881
|
-
const [userTick, setUserTick] =
|
|
6882
|
-
|
|
7327
|
+
const sheetDefs = React14.useRef(/* @__PURE__ */ new Map());
|
|
7328
|
+
const [userTick, setUserTick] = React14.useState(0);
|
|
7329
|
+
React14.useEffect(() => {
|
|
6883
7330
|
if (typeof window === "undefined") return;
|
|
6884
7331
|
const bump = () => setUserTick((n) => n + 1);
|
|
6885
7332
|
window.addEventListener(AUTH_CRYPT_READY, bump);
|
|
6886
7333
|
return () => window.removeEventListener(AUTH_CRYPT_READY, bump);
|
|
6887
7334
|
}, []);
|
|
6888
|
-
const user =
|
|
6889
|
-
|
|
7335
|
+
const user = React14.useMemo(() => getStoredUser() ?? null, [client, userTick]);
|
|
7336
|
+
React14.useEffect(() => {
|
|
6890
7337
|
if (status !== "ready" || mode !== "NEW") return;
|
|
6891
7338
|
const uid = currentUserId(user);
|
|
6892
7339
|
if (!uid) return;
|
|
@@ -6901,7 +7348,7 @@ function useZvolvForm(args) {
|
|
|
6901
7348
|
return m;
|
|
6902
7349
|
});
|
|
6903
7350
|
}, [user, status]);
|
|
6904
|
-
const onBehalfName =
|
|
7351
|
+
const onBehalfName = React14.useCallback((v) => {
|
|
6905
7352
|
if (v == null || v === "") return "";
|
|
6906
7353
|
if (String(v) === "3122") return "Super Admin";
|
|
6907
7354
|
if (isEncString(v)) return "";
|
|
@@ -6911,9 +7358,9 @@ function useZvolvForm(args) {
|
|
|
6911
7358
|
}
|
|
6912
7359
|
return String(v);
|
|
6913
7360
|
}, [user]);
|
|
6914
|
-
const [reloadTick, setReloadTick] =
|
|
7361
|
+
const [reloadTick, setReloadTick] = React14.useState(0);
|
|
6915
7362
|
const loadKey = JSON.stringify([formId, submissionId, mode, !!suppliedDef, reloadTick]);
|
|
6916
|
-
|
|
7363
|
+
React14.useEffect(() => {
|
|
6917
7364
|
let alive = true;
|
|
6918
7365
|
setStatus("loading");
|
|
6919
7366
|
setError(null);
|
|
@@ -6962,13 +7409,13 @@ function useZvolvForm(args) {
|
|
|
6962
7409
|
};
|
|
6963
7410
|
}, [loadKey, client]);
|
|
6964
7411
|
const elements = definition?.elements ?? [];
|
|
6965
|
-
const byId =
|
|
6966
|
-
const labels =
|
|
6967
|
-
const overrides =
|
|
7412
|
+
const byId = React14.useMemo(() => new Map(elements.map((e) => [e.elementId, e])), [elements]);
|
|
7413
|
+
const labels = React14.useMemo(() => elements.map((e) => e.label).filter(Boolean), [elements]);
|
|
7414
|
+
const overrides = React14.useMemo(() => applyRules(elements, values), [elements, values]);
|
|
6968
7415
|
const readOnly = mode === "DISABLED" || !!definition?.configurations?.editingLocked;
|
|
6969
|
-
const hiddenSet =
|
|
6970
|
-
const skipSet =
|
|
6971
|
-
const hiddenByParent =
|
|
7416
|
+
const hiddenSet = React14.useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
|
|
7417
|
+
const skipSet = React14.useMemo(() => new Set((skipTypes ?? []).map((s) => s.toUpperCase())), [skipTypes]);
|
|
7418
|
+
const hiddenByParent = React14.useMemo(() => {
|
|
6972
7419
|
const out = /* @__PURE__ */ new Set();
|
|
6973
7420
|
const visit = (el2) => childIdsOf(el2).forEach((id) => {
|
|
6974
7421
|
if (out.has(id)) return;
|
|
@@ -6983,7 +7430,7 @@ function useZvolvForm(args) {
|
|
|
6983
7430
|
});
|
|
6984
7431
|
return out;
|
|
6985
7432
|
}, [elements, overrides, byId]);
|
|
6986
|
-
const stateOf =
|
|
7433
|
+
const stateOf = React14.useCallback((el2) => {
|
|
6987
7434
|
const o = overrides.get(el2.elementId) ?? {};
|
|
6988
7435
|
return {
|
|
6989
7436
|
hidden: hiddenByParent.has(el2.elementId) || (o.isHidden ?? el2.hidden),
|
|
@@ -6991,7 +7438,7 @@ function useZvolvForm(args) {
|
|
|
6991
7438
|
required: o.isRequired ?? el2.required
|
|
6992
7439
|
};
|
|
6993
7440
|
}, [overrides, readOnly, hiddenByParent]);
|
|
6994
|
-
|
|
7441
|
+
React14.useEffect(() => {
|
|
6995
7442
|
if (status !== "ready") return;
|
|
6996
7443
|
const formulas = elements.filter((e) => resolveFieldType(e) === "INPUT_FORMULA" && e.properties?.formula);
|
|
6997
7444
|
if (!formulas.length) return;
|
|
@@ -7013,7 +7460,7 @@ function useZvolvForm(args) {
|
|
|
7013
7460
|
});
|
|
7014
7461
|
if (Object.keys(next).length) setValues((prev) => ({ ...prev, ...next }));
|
|
7015
7462
|
}, [values, elements, labels, status]);
|
|
7016
|
-
const applyValues =
|
|
7463
|
+
const applyValues = React14.useCallback((data) => {
|
|
7017
7464
|
if (!data || typeof data !== "object") return;
|
|
7018
7465
|
const loose = (s) => String(s).toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
7019
7466
|
const index = /* @__PURE__ */ new Map();
|
|
@@ -7037,7 +7484,7 @@ function useZvolvForm(args) {
|
|
|
7037
7484
|
return merged;
|
|
7038
7485
|
});
|
|
7039
7486
|
}, [elements]);
|
|
7040
|
-
const withBusy =
|
|
7487
|
+
const withBusy = React14.useCallback(async (id, fn) => {
|
|
7041
7488
|
setBusy((b) => new Set(b).add(id));
|
|
7042
7489
|
try {
|
|
7043
7490
|
return await fn();
|
|
@@ -7049,11 +7496,11 @@ function useZvolvForm(args) {
|
|
|
7049
7496
|
});
|
|
7050
7497
|
}
|
|
7051
7498
|
}, []);
|
|
7052
|
-
const triggerBody =
|
|
7499
|
+
const triggerBody = React14.useCallback(
|
|
7053
7500
|
(vals) => buildTriggerBody(elements, vals, { formId: definition?.uuid ?? formId, submissionId: submissionId ?? submittedId }),
|
|
7054
7501
|
[elements, definition, formId, submissionId, submittedId]
|
|
7055
7502
|
);
|
|
7056
|
-
const runFieldAutomations =
|
|
7503
|
+
const runFieldAutomations = React14.useCallback((el2, vals) => {
|
|
7057
7504
|
if (argsRef.current.skipAutomation) return;
|
|
7058
7505
|
const acts = (el2.properties?.actions ?? []).filter((a) => String(a?.type ?? "").toUpperCase() === "TRIGGER_AUTOMATION" && (a.triggerID ?? a.triggerId));
|
|
7059
7506
|
acts.forEach((a) => {
|
|
@@ -7069,7 +7516,7 @@ function useZvolvForm(args) {
|
|
|
7069
7516
|
});
|
|
7070
7517
|
});
|
|
7071
7518
|
}, [client, triggerBody, applyValues, withBusy]);
|
|
7072
|
-
const setValue =
|
|
7519
|
+
const setValue = React14.useCallback((id, value) => {
|
|
7073
7520
|
const el2 = byId.get(id);
|
|
7074
7521
|
const next = { ...valuesRef.current, [id]: value };
|
|
7075
7522
|
valuesRef.current = next;
|
|
@@ -7089,16 +7536,16 @@ function useZvolvForm(args) {
|
|
|
7089
7536
|
if (other.elementId !== id && Array.isArray(watch) && watch.map(String).includes(id)) runFieldAutomations(other, next);
|
|
7090
7537
|
});
|
|
7091
7538
|
}, [byId, elements, runFieldAutomations]);
|
|
7092
|
-
const touch =
|
|
7093
|
-
const clientErrors =
|
|
7539
|
+
const touch = React14.useCallback((id) => setTouched((t) => t.has(id) ? t : new Set(t).add(id)), []);
|
|
7540
|
+
const clientErrors = React14.useMemo(
|
|
7094
7541
|
() => validateForm(elements, values, (el2) => {
|
|
7095
7542
|
const s = stateOf(el2);
|
|
7096
7543
|
return { hidden: s.hidden || hiddenSet.has(el2.elementId) || hiddenSet.has(el2.label), required: s.required };
|
|
7097
7544
|
}),
|
|
7098
7545
|
[elements, values, stateOf, hiddenSet]
|
|
7099
7546
|
);
|
|
7100
|
-
const errors =
|
|
7101
|
-
const validate =
|
|
7547
|
+
const errors = React14.useMemo(() => ({ ...clientErrors, ...Object.fromEntries(Object.entries(serverErrors).filter(([, v]) => v)) }), [clientErrors, serverErrors]);
|
|
7548
|
+
const validate = React14.useCallback((ids) => {
|
|
7102
7549
|
const keys = ids ?? Object.keys(errors);
|
|
7103
7550
|
if (ids) setTouched((t) => {
|
|
7104
7551
|
const n = new Set(t);
|
|
@@ -7114,7 +7561,7 @@ function useZvolvForm(args) {
|
|
|
7114
7561
|
});
|
|
7115
7562
|
return !bad.length;
|
|
7116
7563
|
}, [errors]);
|
|
7117
|
-
const fields =
|
|
7564
|
+
const fields = React14.useMemo(() => elements.map((el2) => {
|
|
7118
7565
|
const t = resolveFieldType(el2);
|
|
7119
7566
|
const kind = fieldKindOf(el2);
|
|
7120
7567
|
const s = stateOf(el2);
|
|
@@ -7164,18 +7611,18 @@ function useZvolvForm(args) {
|
|
|
7164
7611
|
};
|
|
7165
7612
|
return f;
|
|
7166
7613
|
}), [elements, values, errors, stateOf, touched, flagged, serverErrors, hiddenSet, skipSet, setValue, touch, onBehalfName]);
|
|
7167
|
-
const fieldMap2 =
|
|
7168
|
-
const claimed =
|
|
7614
|
+
const fieldMap2 = React14.useMemo(() => new Map(fields.map((f) => [f.id, f])), [fields]);
|
|
7615
|
+
const claimed = React14.useMemo(() => {
|
|
7169
7616
|
const s = /* @__PURE__ */ new Set();
|
|
7170
7617
|
fields.forEach((f) => f.childIds.forEach((c) => s.add(c)));
|
|
7171
7618
|
return s;
|
|
7172
7619
|
}, [fields]);
|
|
7173
|
-
const rootFields =
|
|
7174
|
-
const childrenOf =
|
|
7620
|
+
const rootFields = React14.useMemo(() => fields.filter((f) => !claimed.has(f.id) && !f.hidden && f.kind !== "unknown"), [fields, claimed]);
|
|
7621
|
+
const childrenOf = React14.useCallback((f, visibleOnly = true) => {
|
|
7175
7622
|
const ff = typeof f === "string" ? fieldMap2.get(f) : f;
|
|
7176
7623
|
return (ff?.childIds ?? []).map((id) => fieldMap2.get(id)).filter((c) => !!c && (!visibleOnly || !c.hidden));
|
|
7177
7624
|
}, [fieldMap2]);
|
|
7178
|
-
const runRemoteValidation =
|
|
7625
|
+
const runRemoteValidation = React14.useCallback(async (vals) => {
|
|
7179
7626
|
if (argsRef.current.skipValidations) return {};
|
|
7180
7627
|
const out = {};
|
|
7181
7628
|
for (const el2 of elements) {
|
|
@@ -7197,7 +7644,7 @@ function useZvolvForm(args) {
|
|
|
7197
7644
|
}
|
|
7198
7645
|
return out;
|
|
7199
7646
|
}, [elements, stateOf, client, triggerBody, definition]);
|
|
7200
|
-
const postSheetRows =
|
|
7647
|
+
const postSheetRows = React14.useCallback(async (vals, parentId) => {
|
|
7201
7648
|
const out = { ...vals };
|
|
7202
7649
|
for (const el2 of elements) {
|
|
7203
7650
|
const t = resolveFieldType(el2);
|
|
@@ -7226,7 +7673,7 @@ function useZvolvForm(args) {
|
|
|
7226
7673
|
}
|
|
7227
7674
|
return out;
|
|
7228
7675
|
}, [elements, client]);
|
|
7229
|
-
const submit =
|
|
7676
|
+
const submit = React14.useCallback(async () => {
|
|
7230
7677
|
if (!definition || readOnly) return null;
|
|
7231
7678
|
setSubmitAttempted(true);
|
|
7232
7679
|
setSubmitError(null);
|
|
@@ -7265,7 +7712,7 @@ function useZvolvForm(args) {
|
|
|
7265
7712
|
setSubmitting(false);
|
|
7266
7713
|
}
|
|
7267
7714
|
}, [definition, readOnly, clientErrors, runRemoteValidation, mode, postSheetRows, submissionId, elements, client]);
|
|
7268
|
-
const reset =
|
|
7715
|
+
const reset = React14.useCallback(() => {
|
|
7269
7716
|
setValues(initialRef.current);
|
|
7270
7717
|
setTouched(/* @__PURE__ */ new Set());
|
|
7271
7718
|
setFlagged(/* @__PURE__ */ new Set());
|
|
@@ -7275,8 +7722,8 @@ function useZvolvForm(args) {
|
|
|
7275
7722
|
setDirty(false);
|
|
7276
7723
|
if (definition) setStatus("ready");
|
|
7277
7724
|
}, [definition]);
|
|
7278
|
-
const apiRef =
|
|
7279
|
-
const runAction =
|
|
7725
|
+
const apiRef = React14.useRef(null);
|
|
7726
|
+
const runAction = React14.useCallback(async (field2, choice) => {
|
|
7280
7727
|
const p = field2.element.properties ?? {};
|
|
7281
7728
|
const subType = String(field2.element.attributes?.subType ?? "");
|
|
7282
7729
|
const rawType = p.actionType ?? (subType && subType !== field2.element.type && subType !== field2.type ? subType : "") ?? p.subType;
|
|
@@ -7433,14 +7880,14 @@ function useZvolvForm(args) {
|
|
|
7433
7880
|
}
|
|
7434
7881
|
}
|
|
7435
7882
|
}, [fieldMap2, submit, client, triggerBody, applyValues, withBusy, setValue, elements, definition, formId, submissionId, submittedId]);
|
|
7436
|
-
const reload =
|
|
7437
|
-
const [sheetVersion, setSheetVersion] =
|
|
7438
|
-
const registerSheet =
|
|
7883
|
+
const reload = React14.useCallback(() => setReloadTick((n) => n + 1), []);
|
|
7884
|
+
const [sheetVersion, setSheetVersion] = React14.useState(0);
|
|
7885
|
+
const registerSheet = React14.useCallback((id, def) => {
|
|
7439
7886
|
if (sheetDefs.current.get(id) === def) return;
|
|
7440
7887
|
sheetDefs.current.set(id, def);
|
|
7441
7888
|
setSheetVersion((n) => n + 1);
|
|
7442
7889
|
}, []);
|
|
7443
|
-
const sheetDefinition =
|
|
7890
|
+
const sheetDefinition = React14.useCallback((id) => sheetDefs.current.get(id), []);
|
|
7444
7891
|
const api = {
|
|
7445
7892
|
status,
|
|
7446
7893
|
error,
|
|
@@ -7516,10 +7963,10 @@ function useTrialField(form, field2) {
|
|
|
7516
7963
|
const type = t === "INPUT_TIME_SAMPLING" ? "time" : t === "INPUT_TASK_ANALYSIS" ? "task" : "percent";
|
|
7517
7964
|
const props = field2.element.properties ?? {};
|
|
7518
7965
|
const value = field2.value && typeof field2.value === "object" ? field2.value : null;
|
|
7519
|
-
const conf =
|
|
7966
|
+
const conf = React14.useMemo(() => ({ ...props, ...value?.conf ?? {} }), [props, value?.conf]);
|
|
7520
7967
|
const disabled = field2.disabled || form.readOnly;
|
|
7521
7968
|
const set = (v) => form.setValue(field2.id, v);
|
|
7522
|
-
const [active, setActive] =
|
|
7969
|
+
const [active, setActive] = React14.useState();
|
|
7523
7970
|
const maxTrials = Number(conf.maximumNumberOfTrials ?? (type === "task" ? 5 : 10));
|
|
7524
7971
|
const pcResp = type === "percent" && Array.isArray(value?.buttonResp) ? value.buttonResp : [];
|
|
7525
7972
|
const pcTrial = type === "percent" ? Number(conf.currentTrial ?? pcResp.length) || pcResp.length : 0;
|
|
@@ -7529,11 +7976,11 @@ function useTrialField(form, field2) {
|
|
|
7529
7976
|
};
|
|
7530
7977
|
const timeLimit = Number(conf.timeLimit ?? 300);
|
|
7531
7978
|
const interval = Number(conf.timeInterval ?? 30) || 30;
|
|
7532
|
-
const [timer, setTimer] =
|
|
7533
|
-
const [running, setRunning] =
|
|
7534
|
-
const timerRef =
|
|
7979
|
+
const [timer, setTimer] = React14.useState(Number(value?.lastStatus?.timer ?? 0));
|
|
7980
|
+
const [running, setRunning] = React14.useState(false);
|
|
7981
|
+
const timerRef = React14.useRef(timer);
|
|
7535
7982
|
timerRef.current = timer;
|
|
7536
|
-
|
|
7983
|
+
React14.useEffect(() => {
|
|
7537
7984
|
if (!running) return;
|
|
7538
7985
|
const id = setInterval(() => setTimer((p) => {
|
|
7539
7986
|
const n = p + 1;
|
|
@@ -7553,7 +8000,7 @@ function useTrialField(form, field2) {
|
|
|
7553
8000
|
buttonResp: resp,
|
|
7554
8001
|
lastStatus: { timer: at, intervalCount: Math.floor(at / interval), formattedTimer: fmt(at), currentIntervalBound: bound(at) }
|
|
7555
8002
|
});
|
|
7556
|
-
const steps =
|
|
8003
|
+
const steps = React14.useMemo(() => Object.keys(conf.steps ?? {}), [conf.steps]);
|
|
7557
8004
|
const chain = conf.type === "backward" ? "backward" : conf.type === "whole" ? "whole" : "forward";
|
|
7558
8005
|
const taResp = type === "task" && value?.buttonResp && typeof value.buttonResp === "object" && !Array.isArray(value.buttonResp) ? value.buttonResp : {};
|
|
7559
8006
|
const taTrial = type === "task" ? parseInt(String(value?.currentStatus?.currentTrial ?? "0").replace("Trial ", ""), 10) || 0 : 0;
|
|
@@ -7568,8 +8015,8 @@ function useTrialField(form, field2) {
|
|
|
7568
8015
|
const currentTrial = type === "percent" ? pcTrial : type === "task" ? taTrial : tsResp.length;
|
|
7569
8016
|
const complete = type === "time" ? timer >= timeLimit : currentTrial >= maxTrials;
|
|
7570
8017
|
const rb = type === "task" ? conf.steps?.[stepName]?.ResponseButtons : conf.ResponseButtons;
|
|
7571
|
-
const buttons =
|
|
7572
|
-
const record =
|
|
8018
|
+
const buttons = React14.useMemo(() => toButtons(rb), [rb]);
|
|
8019
|
+
const record = React14.useCallback((response, promptLabel) => {
|
|
7573
8020
|
if (disabled) return;
|
|
7574
8021
|
if (type === "percent") {
|
|
7575
8022
|
if (pcTrial >= maxTrials) return;
|
|
@@ -7601,16 +8048,16 @@ function useTrialField(form, field2) {
|
|
|
7601
8048
|
}
|
|
7602
8049
|
setActive(void 0);
|
|
7603
8050
|
}, [disabled, type, pcTrial, maxTrials, pcResp, tsResp, taResp, taTrial, taStep, steps, stepName, chain, value, conf]);
|
|
7604
|
-
const respond =
|
|
8051
|
+
const respond = React14.useCallback((label) => {
|
|
7605
8052
|
const b = buttons.find((x) => x.label === label);
|
|
7606
8053
|
if (!b || b.disabled) return;
|
|
7607
8054
|
if (b.prompts.length) setActive(label);
|
|
7608
8055
|
else record(label);
|
|
7609
8056
|
}, [buttons, record]);
|
|
7610
|
-
const prompt =
|
|
8057
|
+
const prompt = React14.useCallback((label) => {
|
|
7611
8058
|
if (active) record(active, label);
|
|
7612
8059
|
}, [active, record]);
|
|
7613
|
-
const prev =
|
|
8060
|
+
const prev = React14.useCallback(() => {
|
|
7614
8061
|
if (disabled) return;
|
|
7615
8062
|
if (type === "percent" && pcTrial > 0) {
|
|
7616
8063
|
set({ conf: { ...value?.conf ?? { maximumNumberOfTrials: maxTrials, ResponseButtons: conf.ResponseButtons ?? {} }, currentTrial: pcTrial - 1 }, buttonResp: pcResp.slice(0, -1) });
|
|
@@ -7620,7 +8067,7 @@ function useTrialField(form, field2) {
|
|
|
7620
8067
|
taEmit(resp, taTrial - 1, firstStep);
|
|
7621
8068
|
}
|
|
7622
8069
|
}, [disabled, type, pcTrial, pcResp, taTrial, taResp, firstStep, value, conf, maxTrials]);
|
|
7623
|
-
const reset =
|
|
8070
|
+
const reset = React14.useCallback(() => {
|
|
7624
8071
|
if (type !== "time" || disabled) return;
|
|
7625
8072
|
setTimer(0);
|
|
7626
8073
|
setRunning(false);
|
|
@@ -7666,10 +8113,10 @@ function useTrialField(form, field2) {
|
|
|
7666
8113
|
chain
|
|
7667
8114
|
};
|
|
7668
8115
|
}
|
|
7669
|
-
var FormKitSlot =
|
|
8116
|
+
var FormKitSlot = React14__default.default.createContext(null);
|
|
7670
8117
|
function useKit(local) {
|
|
7671
|
-
const ctx =
|
|
7672
|
-
return
|
|
8118
|
+
const ctx = React14__default.default.useContext(FormKitSlot);
|
|
8119
|
+
return React14__default.default.useMemo(() => ({ ...ctx ?? {}, ...local ?? {} }), [ctx, local]);
|
|
7673
8120
|
}
|
|
7674
8121
|
var FormFields = ({ form, fields, kit: local }) => {
|
|
7675
8122
|
const kit = useKit(local);
|
|
@@ -7957,12 +8404,12 @@ var TabWidget = ({
|
|
|
7957
8404
|
render
|
|
7958
8405
|
}) => {
|
|
7959
8406
|
const t = useTokens();
|
|
7960
|
-
const config =
|
|
8407
|
+
const config = React14.useMemo(
|
|
7961
8408
|
() => resolveWidgetDefaults(t, "TAB", parseConfig(configurations)),
|
|
7962
8409
|
[t, configurations]
|
|
7963
8410
|
);
|
|
7964
8411
|
const chrome = resolveChromeConfig(config);
|
|
7965
|
-
const tabs =
|
|
8412
|
+
const tabs = React14.useMemo(() => {
|
|
7966
8413
|
const declared = config?.tabs ?? config?.childWidget ?? [];
|
|
7967
8414
|
return declared.map((tab, i) => ({
|
|
7968
8415
|
key: String(tab?.key ?? tab?.id ?? i),
|
|
@@ -7970,7 +8417,7 @@ var TabWidget = ({
|
|
|
7970
8417
|
widget: tab?.widget ?? tab
|
|
7971
8418
|
}));
|
|
7972
8419
|
}, [config]);
|
|
7973
|
-
const [activeKey, setActiveKey] =
|
|
8420
|
+
const [activeKey, setActiveKey] = React14.useState("");
|
|
7974
8421
|
const current = activeKey || tabs[0]?.key || "";
|
|
7975
8422
|
const args = { tabs, activeKey: current, setActiveKey, tokens: t, config };
|
|
7976
8423
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -8030,7 +8477,7 @@ var FormWidget = ({
|
|
|
8030
8477
|
onSubmitted
|
|
8031
8478
|
}) => {
|
|
8032
8479
|
const t = useTokens();
|
|
8033
|
-
const config =
|
|
8480
|
+
const config = React14.useMemo(
|
|
8034
8481
|
() => resolveWidgetDefaults(t, "FORM", parseConfig(configurations)),
|
|
8035
8482
|
[t, configurations]
|
|
8036
8483
|
);
|
|
@@ -8071,12 +8518,12 @@ var FilterPanelWidget = ({
|
|
|
8071
8518
|
render
|
|
8072
8519
|
}) => {
|
|
8073
8520
|
const t = useTokens();
|
|
8074
|
-
const config =
|
|
8521
|
+
const config = React14.useMemo(
|
|
8075
8522
|
() => resolveWidgetDefaults(t, "FILTER_PANEL", parseConfig(configurations)),
|
|
8076
8523
|
[t, configurations]
|
|
8077
8524
|
);
|
|
8078
8525
|
const chrome = resolveChromeConfig(config);
|
|
8079
|
-
const controls =
|
|
8526
|
+
const controls = React14.useMemo(() => {
|
|
8080
8527
|
const declared = config?.filters ?? config?.controls ?? [];
|
|
8081
8528
|
return declared.map((f, i) => ({
|
|
8082
8529
|
key: String(f?.key ?? f?.field ?? f?.masterfield ?? i),
|
|
@@ -8085,7 +8532,7 @@ var FilterPanelWidget = ({
|
|
|
8085
8532
|
options: f?.options
|
|
8086
8533
|
}));
|
|
8087
8534
|
}, [config]);
|
|
8088
|
-
const [draft, setDraft] =
|
|
8535
|
+
const [draft, setDraft] = React14.useState({});
|
|
8089
8536
|
const values = { ...draft, ...value || {} };
|
|
8090
8537
|
const setValue = (key, v) => {
|
|
8091
8538
|
const next = { ...values, [key]: v };
|
|
@@ -8150,7 +8597,7 @@ var ZaiChatWidget = ({
|
|
|
8150
8597
|
render
|
|
8151
8598
|
}) => {
|
|
8152
8599
|
const t = useTokens();
|
|
8153
|
-
const config =
|
|
8600
|
+
const config = React14.useMemo(
|
|
8154
8601
|
() => resolveWidgetDefaults(t, "ZAI_CHAT", parseConfig(configurations)),
|
|
8155
8602
|
[t, configurations]
|
|
8156
8603
|
);
|
|
@@ -8381,7 +8828,7 @@ function buildDashFilters(values = [], dateRange, operator = "AND") {
|
|
|
8381
8828
|
}
|
|
8382
8829
|
function dateRangeFor(token) {
|
|
8383
8830
|
const f = (d) => d.format("YYYY-MM-DD");
|
|
8384
|
-
const now =
|
|
8831
|
+
const now = dayjs4__default.default();
|
|
8385
8832
|
switch (token) {
|
|
8386
8833
|
case "TODAY":
|
|
8387
8834
|
return { start: f(now), end: f(now) };
|
|
@@ -8677,7 +9124,7 @@ function inspectForm(form) {
|
|
|
8677
9124
|
};
|
|
8678
9125
|
}
|
|
8679
9126
|
function useFormInspector(form) {
|
|
8680
|
-
return
|
|
9127
|
+
return React14.useMemo(() => inspectForm(form), [form.definition, form.values, form.sheetVersion, form.fields]);
|
|
8681
9128
|
}
|
|
8682
9129
|
|
|
8683
9130
|
// src/forms/authoring.ts
|
|
@@ -8858,8 +9305,10 @@ exports.ZvolvForm = ZvolvForm;
|
|
|
8858
9305
|
exports.ZvolvProvider = ZvolvProvider;
|
|
8859
9306
|
exports.ZvolvThemeProvider = ZvolvThemeProvider;
|
|
8860
9307
|
exports.ZvolvWidget = ZvolvWidget;
|
|
9308
|
+
exports.addPeriod = addPeriod;
|
|
8861
9309
|
exports.aggregate = aggregate;
|
|
8862
9310
|
exports.alignSummaryToSeries = alignSummaryToSeries;
|
|
9311
|
+
exports.applyCumulative = applyCumulative;
|
|
8863
9312
|
exports.applyLocalColumnFilters = applyLocalColumnFilters;
|
|
8864
9313
|
exports.applyLocalSearch = applyLocalSearch;
|
|
8865
9314
|
exports.applyLocalSort = applyLocalSort;
|
|
@@ -8889,6 +9338,7 @@ exports.createSubmission = createSubmission;
|
|
|
8889
9338
|
exports.createZvolvRuntime = createZvolvRuntime;
|
|
8890
9339
|
exports.dashboardDefaultDateToken = dashboardDefaultDateToken;
|
|
8891
9340
|
exports.dashboardFilterDefs = dashboardFilterDefs;
|
|
9341
|
+
exports.dateDiff = dateDiff;
|
|
8892
9342
|
exports.dateRangeFor = dateRangeFor;
|
|
8893
9343
|
exports.defaultTokens = defaultTokens;
|
|
8894
9344
|
exports.defineConfig = defineConfig;
|
|
@@ -8908,6 +9358,7 @@ exports.fieldKind = fieldKind;
|
|
|
8908
9358
|
exports.fieldKindOf = fieldKindOf;
|
|
8909
9359
|
exports.fillDateGaps = fillDateGaps;
|
|
8910
9360
|
exports.filterableFields = filterableFields;
|
|
9361
|
+
exports.flattenPercentiles = flattenPercentiles;
|
|
8911
9362
|
exports.formSource = formSource;
|
|
8912
9363
|
exports.formWidget = formWidget;
|
|
8913
9364
|
exports.formatAmountValue = formatAmountValue;
|
|
@@ -8940,7 +9391,9 @@ exports.openFormRequest = openFormRequest;
|
|
|
8940
9391
|
exports.operatorsForKind = operatorsForKind;
|
|
8941
9392
|
exports.optionFilterRules = optionFilterRules;
|
|
8942
9393
|
exports.parseConfig = parseConfig;
|
|
9394
|
+
exports.parseDateValue = parseDateValue;
|
|
8943
9395
|
exports.parseDeep = parseDeep;
|
|
9396
|
+
exports.percentileKey = percentileKey;
|
|
8944
9397
|
exports.pivot = pivot;
|
|
8945
9398
|
exports.renderHtmlVariables = renderHtmlVariables;
|
|
8946
9399
|
exports.resetQueryUser = resetQueryUser;
|
|
@@ -8963,6 +9416,7 @@ exports.ruleMatches = ruleMatches;
|
|
|
8963
9416
|
exports.rulesOf = rulesOf;
|
|
8964
9417
|
exports.searchPeople = searchPeople;
|
|
8965
9418
|
exports.serializeElementValue = serializeElementValue;
|
|
9419
|
+
exports.startOfPeriod = startOfPeriod;
|
|
8966
9420
|
exports.statusTone = statusTone;
|
|
8967
9421
|
exports.submissionIdOf = submissionIdOf;
|
|
8968
9422
|
exports.submissionToValues = submissionToValues;
|