@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/dist/index.cjs CHANGED
@@ -1,28 +1,30 @@
1
1
  'use strict';
2
2
 
3
- var React13 = require('react');
3
+ var React14 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
- var dayjs8 = require('dayjs');
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 React13__default = /*#__PURE__*/_interopDefault(React13);
13
- var dayjs8__default = /*#__PURE__*/_interopDefault(dayjs8);
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 = React13.createContext(null);
21
+ var RuntimeContext = React14.createContext(null);
20
22
  var ZvolvProvider = ({ runtime, children }) => {
21
- const value = React13.useMemo(() => runtime, [runtime]);
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 = React13.useContext(RuntimeContext);
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
- dayjs8__default.default.extend(utc__default.default);
131
- dayjs8__default.default.extend(weekOfYear__default.default);
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 = dayjs8__default.default();
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
- dayjs8__default.default.extend(utc__default.default);
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 = dayjs8__default.default();
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 = dayjs8__default.default();
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 dayjs8__default.default(oprd, pattern).format("YYYY-MM-DD");
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
- if (mfType === "FORM_SEARCH" || mfType === "INPUT_TAG") {
1000
- sourceObj[row.title] = { terms: { field: resolveFormSearchViewField(row.title) } };
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: `${row.title}.mediaKey.keyword` } };
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: `${row.title}.keyword` } };
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: row.title } };
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.title } };
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 = sumary.title;
1211
+ let aggField = esField;
1068
1212
  if (sumary.masterFieldType === "FORM_SEARCH" && (sumary.type === "value_count" || sumary.type === "cardinality")) {
1069
- aggField = resolveFormSearchViewField(sumary.title);
1213
+ aggField = resolveFormSearchViewField(esField);
1070
1214
  } else if (sumary.masterFieldType === "FILE_UPLOAD_V2") {
1071
- aggField = `${sumary.title}.mediaKey.keyword`;
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 = `${sumary.title}.keyword`;
1218
+ aggField = `${esField}.keyword`;
1075
1219
  }
1076
- const usesRawField = aggField === sumary.title;
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(sumary.title);
1238
+ if (usesRawField) addBucketSort(esField);
1095
1239
  } else {
1096
1240
  summaryAggs[sumary.value][aggType] = { field: aggField };
1097
- if (usesRawField) addBucketSort(sumary.title);
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 aggregate(f, type = "sum", outputName = f.header) {
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 = dayjs8__default.default()) {
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 = dayjs8__default.default(`${y}-${mm}-${dd}`);
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 = dayjs8__default.default(v);
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 = dayjs8__default.default(raw2).format(FMT);
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 = React13.createContext(defaultTokens);
2530
+ var TokensContext = React14.createContext(defaultTokens);
2342
2531
  var ZvolvThemeProvider = ({ tokens, children }) => {
2343
- const value = React13.useMemo(() => mergeTokens(defaultTokens, tokens), [tokens]);
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 React13.useContext(TokensContext);
2536
+ return React14.useContext(TokensContext);
2348
2537
  }
2349
- var TemplateContext = React13.createContext({});
2538
+ var TemplateContext = React14.createContext({});
2350
2539
  var TemplateProvider = ({ templates, children }) => {
2351
- const parent = React13.useContext(TemplateContext);
2352
- const value = React13.useMemo(() => ({ ...parent, ...templates || {} }), [parent, templates]);
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 = React13.useContext(TemplateContext);
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 = React13__default.default.createContext(null);
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 = React13__default.default.useContext(WidgetStateSlot);
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 fillDateGaps({ rows, xKey, series, unit, range }) {
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 = dayjs8__default.default(row[xKey]);
2689
- if (d.isValid()) byPeriod.set(d.startOf(unit).format(keyFormat), row);
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
- let cursor = range.start;
2693
- const useTimestamp = isLikelyTimestamp(rows[0]?.[xKey]);
2694
- while (cursor.isBefore(range.end) || cursor.isSame(range.end, unit)) {
2695
- const key = cursor.format(keyFormat);
2696
- const existing = byPeriod.get(key);
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.add(1, unit);
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] = React13.useState(value);
2751
- React13.useEffect(() => {
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] = React13.useState([]);
2801
- const [userLabels, setUserLabels] = React13.useState({});
2802
- const [initialLoading, setInitialLoading] = React13.useState(true);
2803
- const [loading, setLoading] = React13.useState(false);
2804
- const [error, setError] = React13.useState(null);
2805
- const [nonce, setNonce] = React13.useState(0);
2806
- const requestedUserIds = React13.useRef(/* @__PURE__ */ new Set());
2807
- const reqSeq = React13.useRef(0);
2808
- const config = React13.useMemo(() => parseConfig(configurations), [configurations]);
2809
- const series = React13.useMemo(() => resolveSeries(config), [config]);
2810
- const xKey = React13.useMemo(() => resolveXKey(series), [series]);
2811
- const configured = React13.useMemo(() => isDataSourceConfigured(dataSource), [dataSource]);
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 = React13.useMemo(() => ({
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 normalize = React13.useCallback((incoming) => {
2829
- const withPostOps = applyPostOperations(incoming, dataSource?.pivot?.postOperations);
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 range = dateGap.start && dateGap.end ? { start: dayjs8__default.default(dateGap.start).startOf(dateGap.unit), end: dayjs8__default.default(dateGap.end).startOf(dateGap.unit) } : null;
2833
- return fillDateGaps({ rows: numeric, xKey, series, unit: dateGap.unit, range });
2834
- }, [dataSource, series, xKey, dateGap]);
2835
- React13.useEffect(() => {
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 formId = ds.formId ?? ds.fields?.[0]?.formID;
2862
- const withFormId = formId ? { ...ds, formId } : ds;
2863
- const merged = mergeHostFilters(localFilters, dashFilters);
2864
- const cfgWithDates = { ...config, cdateFilters };
2865
- const dashboardFilters = rt.formatDashboardFilters ? [...rt.formatDashboardFilters(cfgWithDates, withFormId, merged) || []] : [];
2866
- if (remote.widgetFilters?.length) {
2867
- dashboardFilters.push(...buildWidgetFilterClauses(withFormId, remote.widgetFilters));
2868
- }
2869
- if (remote.columnFilters && Object.keys(remote.columnFilters).length) {
2870
- dashboardFilters.push(...rt.formatHeaderFilters ? rt.formatHeaderFilters(withFormId, remote.columnFilters) || [] : buildColumnFilterClauses(withFormId, remote.columnFilters));
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 = React13.useMemo(() => {
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
- React13.useEffect(() => {
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 = React13.useCallback((value) => {
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 = React13.useMemo(() => {
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: React13.useCallback(() => setNonce((n) => n + 1), []),
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] = React13.useState(initial);
3381
+ const [inner, setInner] = React14.useState(initial);
3004
3382
  const isControlled = controlled !== void 0;
3005
3383
  const value = isControlled ? controlled : inner;
3006
- const set = React13.useCallback((v) => {
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 = React13.useRef({ search, columnFilters, sort, widgetFilters });
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 = React13.useCallback((patch) => {
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 = React13.useCallback((v) => {
3419
+ const setSearch = React14.useCallback((v) => {
3042
3420
  setSearchRaw(v);
3043
3421
  report({ search: v });
3044
3422
  }, [setSearchRaw, report]);
3045
- const setColumnFilters = React13.useCallback((v) => {
3423
+ const setColumnFilters = React14.useCallback((v) => {
3046
3424
  setColumnFiltersRaw(v);
3047
3425
  report({ columnFilters: v });
3048
3426
  }, [setColumnFiltersRaw, report]);
3049
- const setColumnFilter = React13.useCallback((key, values) => {
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 = React13.useCallback((v) => {
3433
+ const setSort = React14.useCallback((v) => {
3056
3434
  setSortRaw(v);
3057
3435
  report({ sort: v });
3058
3436
  }, [setSortRaw, report]);
3059
- const toggleSort = React13.useCallback((key) => {
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 = React13.useCallback((v) => {
3443
+ const setWidgetFilters = React14.useCallback((v) => {
3066
3444
  setWidgetFiltersRaw(v);
3067
3445
  report({ widgetFilters: v });
3068
3446
  }, [setWidgetFiltersRaw, report]);
3069
- const addWidgetFilter = React13.useCallback((f) => {
3447
+ const addWidgetFilter = React14.useCallback((f) => {
3070
3448
  setWidgetFilters([...latest.current.widgetFilters, f]);
3071
3449
  }, [setWidgetFilters]);
3072
- const removeWidgetFilter = React13.useCallback((index) => {
3450
+ const removeWidgetFilter = React14.useCallback((index) => {
3073
3451
  setWidgetFilters(latest.current.widgetFilters.filter((_2, i) => i !== index));
3074
3452
  }, [setWidgetFilters]);
3075
- const clearAll = React13.useCallback(() => {
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 React13.useMemo(() => ({
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 = React13.useMemo(() => {
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 = React13.useMemo(
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 = React13.useRef({ ds: "", values: /* @__PURE__ */ new Map() });
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 = React13.useMemo(() => filterableFields(props.dataSource), [dsKey]);
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(React13__default.default.Fragment, { children: props.renderSortIndicator ? props.renderSortIndicator(args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultSortIndicator, { ...args }) }, "sort")
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(React13__default.default.Fragment, { children: props.renderColumnFilter ? props.renderColumnFilter(args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultColumnFilter, { ...args }) }, "filter")
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] = React13.useState(false);
3335
- const ref = React13.useRef(null);
3336
- React13.useEffect(() => {
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] = React13.useState("");
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] = React13.useState("");
3427
- const [operator, setOperator] = React13.useState("");
3428
- const [value, setValue] = React13.useState("");
3429
- const [value2, setValue2] = React13.useState("");
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 React13.useMemo(() => {
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 { title, description, emptyMessage, unconfiguredMessage, className, style, onItemClick, ...dataArgs } = props;
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 vms = useKpiContents({ contents, rows: data.rows, cardTemplate });
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.jsx(
3789
- WidgetShell,
3790
- {
3791
- widgetType: WIDGET_TYPE,
3792
- title: chrome.showHeader ? title ?? chrome.title : void 0,
3793
- description: description ?? chrome.description,
3794
- state,
3795
- error: data.error,
3796
- emptyMessage: emptyMessage ?? chrome.emptyMessage,
3797
- unconfiguredMessage,
3798
- className,
3799
- style,
3800
- actions: ix.toolbar,
3801
- ...ix.shell,
3802
- 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)) })
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 = React13.useMemo(() => {
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 = React13__default.default.createElement(override, args);
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 } = React13.useMemo(() => {
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 ? React13__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultRanking, { ...args })
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 = React13.useMemo(() => data.rows.map((row) => {
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 React13.useMemo(() => {
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 = React13.useMemo(() => resolveCollectionMapping(config, data.meta.series), [config, data.meta.series]);
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 ? React13__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultList, { ...args })
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 React13.useMemo(() => {
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 = dayjs8__default.default(v);
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 React13.useMemo(() => {
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 && dayjs8__default.default(start).startOf("day").isSame(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 = React13.useMemo(() => {
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 = React13.useMemo(() => {
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 ? React13__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultTimeline, { ...args })
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 ? dayjs8__default.default(e.start).format("DD MMM YYYY") : "",
4629
- e.end ? ` \u2192 ${dayjs8__default.default(e.end).format("DD MMM YYYY")}` : "",
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 = React13.useMemo(() => {
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 ? React13__default.default.createElement(override, args) : /* @__PURE__ */ jsxRuntime.jsx(DefaultStepper, { ...args })
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 = dayjs8__default.default(value);
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 = React13.useMemo(
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 = React13.useMemo(() => interpolate(raw2, data.rows[0]), [raw2, data.rows]);
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 = React13.useMemo(() => data.rows.flatMap((row, i) => {
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 = React13.useMemo(() => {
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 = React13.useMemo(() => {
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 = React13.useMemo(() => buildTree(
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 = dayjs8__default.default();
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 ? dayjs8__default.default(v) : typeof v === "string" && v ? dayjs8__default.default(v) : null;
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) => dayjs8__default.default(v).format("D MMM YYYY")).join(" \u2013 ");
6032
- if (t === "DATE_PICKER") return dayjs8__default.default(value).isValid() ? dayjs8__default.default(value).format("D MMM YYYY") : String(value);
6033
- if (t === "DATE_TIME_PICKER") return dayjs8__default.default(value).isValid() ? dayjs8__default.default(value).format("D MMM YYYY, HH:mm") : String(value);
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 = React13.useMemo(() => field2.kind === "lookup" ? lookupConfigOf(el2) : null, [field2.kind, el2]);
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 = React13.useMemo(() => optionFilterRules(elements, field2.id), [elements, field2.id]);
6501
- const dep = React13.useMemo(() => cfg ? lookupDependencyFilters(cfg, rules, elements, form.values) : { filters: [], satisfied: true }, [cfg, rules, elements, form.values]);
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] = React13.useState("");
6506
- const [q, setQ] = React13.useState("");
6507
- React13.useEffect(() => {
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] = React13.useState([]);
6512
- const [loading, setLoading] = React13.useState(false);
6513
- const [error, setError] = React13.useState(null);
6514
- const [opened, setOpened] = React13.useState(loadOn === "mount");
6515
- const [nonce, setNonce] = React13.useState(0);
6516
- const lastFetch = React13.useRef(0);
6517
- const maxAge = React13.useRef(1e4);
6518
- const waitingFor = React13.useMemo(() => {
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
- React13.useEffect(() => {
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 = React13.useCallback(() => {
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 = React13.useCallback(() => {
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 = React13.useRef(null);
6569
- React13.useEffect(() => {
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 = React13.useMemo(() => {
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 = React13.useRef(/* @__PURE__ */ new Map());
7032
+ const pickedCache = React14.useRef(/* @__PURE__ */ new Map());
6586
7033
  const current = field2.value;
6587
- const selected = React13.useMemo(() => {
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 = React13.useCallback((picked) => {
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 = React13.useRef(() => {
7064
+ const autoPickRef = React14.useRef(() => {
6618
7065
  });
6619
- const lastAutoKey = React13.useRef(null);
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 = React13.useCallback((o) => store(o == null ? [] : Array.isArray(o) ? o : [o]), [store]);
6627
- const isSelected = React13.useCallback((o) => selected.some((s) => optionKey(s.value) === optionKey(o.value)), [selected]);
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 = React13.useCallback((o) => {
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 = React13.useCallback((text2) => {
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] = React13.useState([]);
6666
- const [reject, setReject] = React13.useState(null);
6667
- const storedRef = React13.useRef(stored);
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 = React13.useCallback((list) => {
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 = React13.useCallback((key) => {
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] = React13.useState(null);
6737
- const [loading, setLoading] = React13.useState(!!childId);
6738
- const [error, setError] = React13.useState(childId ? null : "No child form configured");
6739
- React13.useEffect(() => {
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 = React13.useMemo(() => (child?.elements ?? []).filter((e) => !e.hidden && !["COLLAPSE_SECTION", "CONTAINER", "GROUP_VIEW", "TEXT_VIEW", "ACTION_BUTTON"].includes(resolveFieldType(e))).map((e) => ({
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] = React13.useState(null);
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 = React13.useCallback((vals) => {
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 = React13.useCallback((i, col, v) => {
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 = React13.useCallback((i) => {
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 = React13.useMemo(() => {
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 = React13__default.default.createContext(null);
7298
+ var FormApiContext = React14__default.default.createContext(null);
6852
7299
  function useFormApi() {
6853
- const f = React13__default.default.useContext(FormApiContext);
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 = React13.useRef(args);
7309
+ const argsRef = React14.useRef(args);
6863
7310
  argsRef.current = args;
6864
- const [definition, setDefinition] = React13.useState(null);
6865
- const [status, setStatus] = React13.useState("loading");
6866
- const [error, setError] = React13.useState(null);
6867
- const [values, setValues] = React13.useState({});
6868
- const [serverErrors, setServerErrors] = React13.useState({});
6869
- const [touched, setTouched] = React13.useState(/* @__PURE__ */ new Set());
6870
- const [submitAttempted, setSubmitAttempted] = React13.useState(false);
6871
- const [flagged, setFlagged] = React13.useState(/* @__PURE__ */ new Set());
6872
- const [submitting, setSubmitting] = React13.useState(false);
6873
- const [submitError, setSubmitError] = React13.useState(null);
6874
- const [submittedId, setSubmittedId] = React13.useState(null);
6875
- const [dirty, setDirty] = React13.useState(false);
6876
- const [busy, setBusy] = React13.useState(/* @__PURE__ */ new Set());
6877
- const initialRef = React13.useRef({});
6878
- const valuesRef = React13.useRef({});
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 = React13.useRef(/* @__PURE__ */ new Map());
6881
- const [userTick, setUserTick] = React13.useState(0);
6882
- React13.useEffect(() => {
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 = React13.useMemo(() => getStoredUser() ?? null, [client, userTick]);
6889
- React13.useEffect(() => {
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 = React13.useCallback((v) => {
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] = React13.useState(0);
7361
+ const [reloadTick, setReloadTick] = React14.useState(0);
6915
7362
  const loadKey = JSON.stringify([formId, submissionId, mode, !!suppliedDef, reloadTick]);
6916
- React13.useEffect(() => {
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 = React13.useMemo(() => new Map(elements.map((e) => [e.elementId, e])), [elements]);
6966
- const labels = React13.useMemo(() => elements.map((e) => e.label).filter(Boolean), [elements]);
6967
- const overrides = React13.useMemo(() => applyRules(elements, values), [elements, values]);
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 = React13.useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
6970
- const skipSet = React13.useMemo(() => new Set((skipTypes ?? []).map((s) => s.toUpperCase())), [skipTypes]);
6971
- const hiddenByParent = React13.useMemo(() => {
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 = React13.useCallback((el2) => {
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
- React13.useEffect(() => {
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 = React13.useCallback((data) => {
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 = React13.useCallback(async (id, fn) => {
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 = React13.useCallback(
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 = React13.useCallback((el2, vals) => {
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 = React13.useCallback((id, value) => {
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 = React13.useCallback((id) => setTouched((t) => t.has(id) ? t : new Set(t).add(id)), []);
7093
- const clientErrors = React13.useMemo(
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 = React13.useMemo(() => ({ ...clientErrors, ...Object.fromEntries(Object.entries(serverErrors).filter(([, v]) => v)) }), [clientErrors, serverErrors]);
7101
- const validate = React13.useCallback((ids) => {
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 = React13.useMemo(() => elements.map((el2) => {
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 = React13.useMemo(() => new Map(fields.map((f) => [f.id, f])), [fields]);
7168
- const claimed = React13.useMemo(() => {
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 = React13.useMemo(() => fields.filter((f) => !claimed.has(f.id) && !f.hidden && f.kind !== "unknown"), [fields, claimed]);
7174
- const childrenOf = React13.useCallback((f, visibleOnly = true) => {
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 = React13.useCallback(async (vals) => {
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 = React13.useCallback(async (vals, parentId) => {
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 = React13.useCallback(async () => {
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 = React13.useCallback(() => {
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 = React13.useRef(null);
7279
- const runAction = React13.useCallback(async (field2, choice) => {
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 = React13.useCallback(() => setReloadTick((n) => n + 1), []);
7437
- const [sheetVersion, setSheetVersion] = React13.useState(0);
7438
- const registerSheet = React13.useCallback((id, def) => {
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 = React13.useCallback((id) => sheetDefs.current.get(id), []);
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 = React13.useMemo(() => ({ ...props, ...value?.conf ?? {} }), [props, value?.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] = React13.useState();
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] = React13.useState(Number(value?.lastStatus?.timer ?? 0));
7533
- const [running, setRunning] = React13.useState(false);
7534
- const timerRef = React13.useRef(timer);
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
- React13.useEffect(() => {
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 = React13.useMemo(() => Object.keys(conf.steps ?? {}), [conf.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 = React13.useMemo(() => toButtons(rb), [rb]);
7572
- const record = React13.useCallback((response, promptLabel) => {
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 = React13.useCallback((label) => {
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 = React13.useCallback((label) => {
8057
+ const prompt = React14.useCallback((label) => {
7611
8058
  if (active) record(active, label);
7612
8059
  }, [active, record]);
7613
- const prev = React13.useCallback(() => {
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 = React13.useCallback(() => {
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 = React13__default.default.createContext(null);
8116
+ var FormKitSlot = React14__default.default.createContext(null);
7670
8117
  function useKit(local) {
7671
- const ctx = React13__default.default.useContext(FormKitSlot);
7672
- return React13__default.default.useMemo(() => ({ ...ctx ?? {}, ...local ?? {} }), [ctx, local]);
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 = React13.useMemo(
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 = React13.useMemo(() => {
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] = React13.useState("");
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 = React13.useMemo(
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 = React13.useMemo(
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 = React13.useMemo(() => {
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] = React13.useState({});
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 = React13.useMemo(
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 = dayjs8__default.default();
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 React13.useMemo(() => inspectForm(form), [form.definition, form.values, form.sheetVersion, form.fields]);
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;