@campfire-interactive/volume-intelligence-ui 0.9.1 → 0.11.1
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.d.ts +18 -3
- package/dist/index.js +68 -5
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -90,8 +90,12 @@ declare function VolumeImport(props: VolumeImportProps): react.JSX.Element;
|
|
|
90
90
|
|
|
91
91
|
type SourceLayout = 'wide' | 'tall';
|
|
92
92
|
/** `base` = IHS/AFS historical training signal (one active per tenant);
|
|
93
|
-
* `overlay` = EDI-style additive customer-stated demand
|
|
94
|
-
|
|
93
|
+
* `overlay` = EDI-style additive customer-stated demand;
|
|
94
|
+
* `reference` = a second market vendor's curve, drawn beside the active base
|
|
95
|
+
* rather than summed into it. A reference source never drives prediction,
|
|
96
|
+
* never supplies level labels, and is never what an overlay links against —
|
|
97
|
+
* which is why it is a kind of its own rather than a second `base`. */
|
|
98
|
+
type VolumeSourceKind = 'base' | 'overlay' | 'reference';
|
|
95
99
|
interface VolumeSource {
|
|
96
100
|
id: string;
|
|
97
101
|
code: string;
|
|
@@ -272,7 +276,18 @@ interface DetectSegmentsResult {
|
|
|
272
276
|
interface ClearSourceDataResult {
|
|
273
277
|
uploads: number;
|
|
274
278
|
criteria: number;
|
|
275
|
-
|
|
279
|
+
/** Whether the source is now EMPTY. False when the call hit its time budget
|
|
280
|
+
* with rows still to delete — call again to continue.
|
|
281
|
+
*
|
|
282
|
+
* BREAKING in 2026-09: this was the literal `true`, returned regardless of
|
|
283
|
+
* whether anything remained. Batched clearing introduced a third outcome
|
|
284
|
+
* (partial), and a consumer branching on `cleared` would have read it as an
|
|
285
|
+
* empty source. */
|
|
286
|
+
cleared: boolean;
|
|
287
|
+
/** False when the call hit its time budget with rows still to delete. */
|
|
288
|
+
done?: boolean;
|
|
289
|
+
/** Criteria still to delete when `done` is false. */
|
|
290
|
+
remainingCriteria?: number;
|
|
276
291
|
}
|
|
277
292
|
/** Whether a source satisfies every required identity field. The dual-
|
|
278
293
|
* satisfaction rule (explicit automotive_field mapping OR the covering
|
package/dist/index.js
CHANGED
|
@@ -901,7 +901,8 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
|
|
|
901
901
|
/* @__PURE__ */ jsxs4("span", { className: "cfi-vi-card-title", children: [
|
|
902
902
|
/* @__PURE__ */ jsx4("button", { type: "button", className: "cfi-vi-link", onClick: () => onOpenSource(src.id), children: src.name }),
|
|
903
903
|
isActive && /* @__PURE__ */ jsx4(Badge, { variant: "success", children: "Active" }),
|
|
904
|
-
src.kind === "overlay" && /* @__PURE__ */ jsx4(Badge, { variant: "purple", title: "Overlay sources are active whenever data is uploaded \u2014 no Set Active step.", children: "Overlay" })
|
|
904
|
+
src.kind === "overlay" && /* @__PURE__ */ jsx4(Badge, { variant: "purple", title: "Overlay sources are active whenever data is uploaded \u2014 no Set Active step.", children: "Overlay" }),
|
|
905
|
+
src.kind === "reference" && /* @__PURE__ */ jsx4(Badge, { variant: "outline", title: "A second vendor's curve, drawn beside the active base rather than summed into it. Never drives prediction and has no Set Active step.", children: "Reference" })
|
|
905
906
|
] }),
|
|
906
907
|
/* @__PURE__ */ jsxs4("span", { className: "cfi-vi-row", children: [
|
|
907
908
|
/* @__PURE__ */ jsx4(Badge, { variant: "outline", children: src.code }),
|
|
@@ -1664,6 +1665,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, readOnl
|
|
|
1664
1665
|
const [savingLayout, setSavingLayout] = useState7(false);
|
|
1665
1666
|
const [editLayout, setEditLayout] = useState7("wide");
|
|
1666
1667
|
const [editValueDateFormat, setEditValueDateFormat] = useState7("yyyy-MM-dd");
|
|
1668
|
+
const [editKind, setEditKind] = useState7("base");
|
|
1667
1669
|
const [newSegmentLabel, setNewSegmentLabel] = useState7("");
|
|
1668
1670
|
const [newSegmentDataType, setNewSegmentDataType] = useState7("string");
|
|
1669
1671
|
const [addingSegment, setAddingSegment] = useState7(false);
|
|
@@ -1701,6 +1703,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, readOnl
|
|
|
1701
1703
|
setSource(src);
|
|
1702
1704
|
setSelectedKeyId(src.uniqueKeySegmentId ?? "__none__");
|
|
1703
1705
|
setEditLayout(src.layout);
|
|
1706
|
+
setEditKind(src.kind);
|
|
1704
1707
|
setEditValueDateFormat(src.valueDateFormat);
|
|
1705
1708
|
setHasData(uploads.items.some((u) => u.status === "done" || u.status === "done_with_errors"));
|
|
1706
1709
|
setMappingKeys(keys);
|
|
@@ -1769,8 +1772,31 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, readOnl
|
|
|
1769
1772
|
setError(null);
|
|
1770
1773
|
setClearResult(null);
|
|
1771
1774
|
try {
|
|
1772
|
-
|
|
1773
|
-
|
|
1775
|
+
let criteria = 0;
|
|
1776
|
+
let uploads = 0;
|
|
1777
|
+
let passes = 0;
|
|
1778
|
+
const MAX_PASSES = 200;
|
|
1779
|
+
for (; ; ) {
|
|
1780
|
+
const result = await transport.clearSourceData(id, clearConfirm);
|
|
1781
|
+
criteria += result.criteria;
|
|
1782
|
+
uploads += result.uploads;
|
|
1783
|
+
passes += 1;
|
|
1784
|
+
if (result.done !== false || passes >= MAX_PASSES) {
|
|
1785
|
+
if (result.done === false) {
|
|
1786
|
+
setClearResult(
|
|
1787
|
+
`Stopped after ${passes} passes with ${(result.remainingCriteria ?? 0).toLocaleString()} criteria still to delete. ${criteria.toLocaleString()} criteria and ${uploads.toLocaleString()} uploads were removed. Run Clear again to continue.`
|
|
1788
|
+
);
|
|
1789
|
+
} else {
|
|
1790
|
+
setClearResult(
|
|
1791
|
+
`Cleared ${criteria.toLocaleString()} criteria and ${uploads.toLocaleString()} uploads${passes > 1 ? ` over ${passes} passes` : ""}. Configuration (segments, mappings) preserved.`
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
setClearResult(
|
|
1797
|
+
`Clearing\u2026 ${criteria.toLocaleString()} criteria removed so far, ${(result.remainingCriteria ?? 0).toLocaleString()} to go.`
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1774
1800
|
setClearConfirm("");
|
|
1775
1801
|
await load();
|
|
1776
1802
|
} catch (e) {
|
|
@@ -1809,12 +1835,17 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, readOnl
|
|
|
1809
1835
|
setSavingKey(false);
|
|
1810
1836
|
}
|
|
1811
1837
|
}
|
|
1838
|
+
const crossesMeasure = !!source && editKind !== source.kind && (editKind === "overlay" || source.kind === "overlay");
|
|
1812
1839
|
async function saveLayoutAndRouting() {
|
|
1813
1840
|
if (!id) return;
|
|
1814
1841
|
setSavingLayout(true);
|
|
1815
1842
|
setError(null);
|
|
1816
1843
|
try {
|
|
1817
|
-
await transport.updateSource(id, {
|
|
1844
|
+
await transport.updateSource(id, {
|
|
1845
|
+
layout: editLayout,
|
|
1846
|
+
valueDateFormat: editValueDateFormat,
|
|
1847
|
+
kind: editKind
|
|
1848
|
+
});
|
|
1818
1849
|
await load();
|
|
1819
1850
|
} catch (e) {
|
|
1820
1851
|
setError(e instanceof Error ? e.message : "Failed to save layout");
|
|
@@ -1946,9 +1977,41 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, readOnl
|
|
|
1946
1977
|
onChange: (e) => setEditValueDateFormat(e.target.value)
|
|
1947
1978
|
}
|
|
1948
1979
|
)
|
|
1980
|
+
] }),
|
|
1981
|
+
/* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
|
|
1982
|
+
/* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Role" }),
|
|
1983
|
+
/* @__PURE__ */ jsxs8(
|
|
1984
|
+
"select",
|
|
1985
|
+
{
|
|
1986
|
+
className: "cfi-vi-input",
|
|
1987
|
+
value: editKind,
|
|
1988
|
+
disabled: savingLayout || readOnly,
|
|
1989
|
+
onChange: (e) => setEditKind(e.target.value),
|
|
1990
|
+
children: [
|
|
1991
|
+
/* @__PURE__ */ jsx8("option", { value: "base", children: "base \u2014 the tenant's own volume" }),
|
|
1992
|
+
/* @__PURE__ */ jsx8("option", { value: "overlay", children: "overlay \u2014 customer-stated demand" }),
|
|
1993
|
+
/* @__PURE__ */ jsx8("option", { value: "reference", children: "reference \u2014 another vendor's curve" })
|
|
1994
|
+
]
|
|
1995
|
+
}
|
|
1996
|
+
)
|
|
1949
1997
|
] })
|
|
1950
1998
|
] }),
|
|
1951
|
-
/* @__PURE__ */
|
|
1999
|
+
hasData && crossesMeasure && /* @__PURE__ */ jsxs8("p", { className: "cfi-vi-hint", style: { color: "#b45309" }, children: [
|
|
2000
|
+
"This source has data. Changing between ",
|
|
2001
|
+
source.kind,
|
|
2002
|
+
" and ",
|
|
2003
|
+
editKind,
|
|
2004
|
+
" would reinterpret parts as vehicles or the reverse \u2014 clear its data first."
|
|
2005
|
+
] }),
|
|
2006
|
+
/* @__PURE__ */ jsx8(
|
|
2007
|
+
Btn,
|
|
2008
|
+
{
|
|
2009
|
+
size: "sm",
|
|
2010
|
+
onClick: () => void saveLayoutAndRouting(),
|
|
2011
|
+
disabled: savingLayout || readOnly || hasData && crossesMeasure || hasData && editKind === source.kind,
|
|
2012
|
+
children: savingLayout ? "Saving\u2026" : "Save"
|
|
2013
|
+
}
|
|
2014
|
+
)
|
|
1952
2015
|
] }),
|
|
1953
2016
|
/* @__PURE__ */ jsxs8("div", { className: "cfi-vi-panel", children: [
|
|
1954
2017
|
/* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campfire-interactive/volume-intelligence-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Shared Volume Intelligence UI components (volume import) for the Campfire Suite — embedded by the VI webapp and by consuming apps (OMSF) so there is one import surface, not two that drift.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|