@campfire-interactive/volume-intelligence-ui 0.4.0 → 0.5.0

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 CHANGED
@@ -258,6 +258,17 @@ interface ClearSourceDataResult {
258
258
  criteria: number;
259
259
  cleared: true;
260
260
  }
261
+ /** Whether a source satisfies every required identity field. The dual-
262
+ * satisfaction rule (explicit automotive_field mapping OR the covering
263
+ * hierarchy level) is applied server-side, so the UI just renders this. */
264
+ interface RequiredMappingStatus {
265
+ satisfied: boolean;
266
+ /** Required fields with neither an explicit mapping nor a covering level. */
267
+ missing: string[];
268
+ /** True only for wide base sources — the scope of the ingest/activation gate;
269
+ * tall sources carry a minimal identity set and overlays inherit from base. */
270
+ enforced: boolean;
271
+ }
261
272
  /**
262
273
  * Everything the source-management components need from the host. VI-webapp
263
274
  * implements these against its `adminApi`; OMSF implements them against proxy
@@ -296,6 +307,7 @@ interface SourceAdminTransport {
296
307
  setTenantSource(sourceId: string): Promise<TenantVolumeConfig>;
297
308
  listUploads(params?: UploadListParams): Promise<UploadListResponse>;
298
309
  clearSourceData(sourceId: string, confirm: string): Promise<ClearSourceDataResult>;
310
+ getRequiredMappingStatus?(sourceId: string): Promise<RequiredMappingStatus>;
299
311
  }
300
312
 
301
313
  interface VolumeFilesTransport {
package/dist/index.js CHANGED
@@ -1731,6 +1731,18 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1731
1731
  const [mappingRows, setMappingRows] = useState7([]);
1732
1732
  const [savingMapping, setSavingMapping] = useState7(false);
1733
1733
  const [baseHierarchyHint, setBaseHierarchyHint] = useState7({});
1734
+ const [requiredStatus, setRequiredStatus] = useState7(null);
1735
+ async function loadRequiredStatus() {
1736
+ if (!id || !transport.getRequiredMappingStatus) {
1737
+ setRequiredStatus(null);
1738
+ return;
1739
+ }
1740
+ try {
1741
+ setRequiredStatus(await transport.getRequiredMappingStatus(id));
1742
+ } catch {
1743
+ setRequiredStatus(null);
1744
+ }
1745
+ }
1734
1746
  async function load() {
1735
1747
  if (!id) return;
1736
1748
  try {
@@ -1777,6 +1789,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1777
1789
  } else {
1778
1790
  setBaseHierarchyHint({});
1779
1791
  }
1792
+ void loadRequiredStatus();
1780
1793
  } catch (e) {
1781
1794
  setError(e instanceof Error ? e.message : "Failed to load source");
1782
1795
  } finally {
@@ -1817,6 +1830,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1817
1830
  const hResp = await transport.replaceSegmentMappingCategory(id, "hierarchy_level", hierarchy);
1818
1831
  const aResp = await transport.replaceSegmentMappingCategory(id, "automotive_field", automotive);
1819
1832
  setMappingRows([...hResp.items, ...aResp.items]);
1833
+ void loadRequiredStatus();
1820
1834
  } catch (e) {
1821
1835
  setError(e instanceof Error ? e.message : "Failed to save mapping");
1822
1836
  } finally {
@@ -2136,6 +2150,34 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
2136
2150
  /* @__PURE__ */ jsx8("h2", { className: "cfi-vi-h2", children: "Segment Mapping" }),
2137
2151
  isOverlay && /* @__PURE__ */ jsx8("span", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "\xB7 Map at least the levels your base source uses for the overlay merge to land." })
2138
2152
  ] }),
2153
+ requiredStatus?.enforced && (requiredStatus.satisfied ? /* @__PURE__ */ jsxs8(
2154
+ "div",
2155
+ {
2156
+ className: "cfi-vi-row",
2157
+ style: { gap: 6, color: "#15803d", fontSize: "var(--cfi-font-size-sm, 13px)", margin: "4px 0 8px" },
2158
+ children: [
2159
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": true, children: "\u2713" }),
2160
+ " All required mappings are set \u2014 this source can be activated and ingested."
2161
+ ]
2162
+ }
2163
+ ) : /* @__PURE__ */ jsxs8(
2164
+ "div",
2165
+ {
2166
+ className: "cfi-vi-lock",
2167
+ style: { display: "flex", alignItems: "flex-start", gap: 8, margin: "4px 0 8px" },
2168
+ children: [
2169
+ /* @__PURE__ */ jsx8(AlertTriangle3, { size: 14, "aria-hidden": true, style: { marginTop: 2, flexShrink: 0 } }),
2170
+ /* @__PURE__ */ jsxs8("span", { children: [
2171
+ "Missing required mapping",
2172
+ requiredStatus.missing.length === 1 ? "" : "s",
2173
+ ":",
2174
+ " ",
2175
+ requiredStatus.missing.map((m) => /* @__PURE__ */ jsx8("span", { className: "cfi-vi-mono", style: { marginRight: 6 }, children: m }, m)),
2176
+ "\u2014 map these (directly, or via the covering hierarchy level) before this source can be activated or ingest."
2177
+ ] })
2178
+ ]
2179
+ }
2180
+ )),
2139
2181
  source.segments.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "cfi-vi-muted", children: "No segments yet. Add segments on the Structure tab (Detect from file or Add manually) before mapping." }) : /* @__PURE__ */ jsx8(
2140
2182
  UnifiedSegmentMapper,
2141
2183
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@campfire-interactive/volume-intelligence-ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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",