@campfire-interactive/volume-intelligence-ui 0.1.0 → 0.3.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +36 -1
  2. package/dist/index.js +936 -459
  3. package/package.json +43 -43
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ function styleInject(css, { insertAt } = {}) {
25
25
  }
26
26
 
27
27
  // src/styles.css
28
- styleInject(".cfi-volume-import {\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-base, 14px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n max-width: 640px;\n}\n.cfi-volume-import .cfi-vi-field {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 4px);\n}\n.cfi-volume-import .cfi-vi-row {\n display: flex;\n gap: var(--cfi-spacing-md, 12px);\n flex-wrap: wrap;\n}\n.cfi-volume-import .cfi-vi-row .cfi-vi-field {\n flex: 1 1 8rem;\n}\n.cfi-volume-import .cfi-vi-label {\n font-size: var(--cfi-font-size-sm, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-volume-import .cfi-vi-select,\n.cfi-volume-import .cfi-vi-file {\n padding: 6px 8px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n font: inherit;\n color: inherit;\n}\n.cfi-volume-import .cfi-vi-select:focus,\n.cfi-volume-import .cfi-vi-file:focus {\n outline: 2px solid var(--cfi-color-primary-600, #2563eb);\n outline-offset: 1px;\n}\n.cfi-volume-import .cfi-vi-button {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 6px);\n align-self: flex-start;\n padding: 8px 14px;\n border: none;\n border-radius: var(--cfi-radius-md, 6px);\n background: var(--cfi-color-primary-600, #2563eb);\n color: #fff;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n.cfi-volume-import .cfi-vi-button:hover:not(:disabled) {\n background: var(--cfi-color-primary-700, #1d4ed8);\n}\n.cfi-volume-import .cfi-vi-button:disabled {\n background: var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-500, #6b7280);\n cursor: not-allowed;\n}\n.cfi-volume-import .cfi-vi-loading,\n.cfi-volume-import .cfi-vi-alert {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 6px);\n padding: 8px 10px;\n border-radius: var(--cfi-radius-md, 6px);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-alert--error {\n background: var(--cfi-color-gray-100, #fef2f2);\n color: var(--cfi-color-error, #b91c1c);\n}\n.cfi-volume-import .cfi-vi-alert--warn {\n background: #fffbeb;\n color: #92400e;\n align-items: flex-start;\n}\n.cfi-volume-import .cfi-vi-blockers {\n margin: 0;\n padding-left: 1.1rem;\n}\n.cfi-volume-import .cfi-vi-status {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n padding: var(--cfi-spacing-md, 12px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-volume-import .cfi-vi-status-head {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-volume-import .cfi-vi-filename {\n font-weight: 600;\n}\n.cfi-volume-import .cfi-vi-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: var(--cfi-font-size-xs, 11px);\n text-transform: capitalize;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-volume-import .cfi-vi-badge--done {\n background: #dcfce7;\n color: #166534;\n}\n.cfi-volume-import .cfi-vi-badge--done_with_errors {\n background: #fef9c3;\n color: #854d0e;\n}\n.cfi-volume-import .cfi-vi-badge--failed {\n background: #fee2e2;\n color: #991b1b;\n}\n.cfi-volume-import .cfi-vi-badge--processing,\n.cfi-volume-import .cfi-vi-badge--pending,\n.cfi-volume-import .cfi-vi-badge--awaiting_calendar {\n background: #dbeafe;\n color: #1e40af;\n}\n.cfi-volume-import .cfi-vi-link {\n background: none;\n border: none;\n padding: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--cfi-color-primary-600, #2563eb);\n font: inherit;\n cursor: pointer;\n}\n.cfi-volume-import .cfi-vi-stats {\n display: flex;\n gap: var(--cfi-spacing-md, 12px);\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-errors {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-errors th,\n.cfi-volume-import .cfi-vi-errors td {\n text-align: left;\n padding: 4px 8px;\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n vertical-align: top;\n}\n.cfi-volume-import .cfi-vi-spin {\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n@keyframes cfi-vi-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.cfi-vi {\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-base, 14px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n}\n.cfi-vi *,\n.cfi-vi *::before,\n.cfi-vi *::after {\n box-sizing: border-box;\n}\n.cfi-vi .cfi-vi-row {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi .cfi-vi-row--between {\n justify-content: space-between;\n}\n.cfi-vi .cfi-vi-spacer {\n flex: 1;\n}\n.cfi-vi .cfi-vi-muted {\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-vi .cfi-vi-error {\n color: var(--cfi-color-error, #b91c1c);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-mono {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n font-size: var(--cfi-font-size-xs, 11px);\n}\n.cfi-vi .cfi-vi-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border-radius: var(--cfi-radius-md, 6px);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n.cfi-vi .cfi-vi-btn--sm {\n padding: 4px 10px;\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-btn--md {\n padding: 8px 14px;\n}\n.cfi-vi .cfi-vi-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-vi .cfi-vi-btn--primary {\n background: var(--cfi-color-primary-600, #2563eb);\n color: #fff;\n}\n.cfi-vi .cfi-vi-btn--primary:hover:not(:disabled) {\n background: var(--cfi-color-primary-700, #1d4ed8);\n}\n.cfi-vi .cfi-vi-btn--outline {\n background: #fff;\n border-color: var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-vi .cfi-vi-btn--outline:hover:not(:disabled) {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-vi .cfi-vi-btn--ghost {\n background: transparent;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-btn--ghost:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-vi .cfi-vi-btn--destructive {\n background: var(--cfi-color-error, #dc2626);\n color: #fff;\n}\n.cfi-vi .cfi-vi-btn--destructive:hover:not(:disabled) {\n filter: brightness(0.92);\n}\n.cfi-vi .cfi-vi-icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--cfi-color-gray-500, #6b7280);\n border-radius: var(--cfi-radius-sm, 4px);\n display: inline-flex;\n}\n.cfi-vi .cfi-vi-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-vi .cfi-vi-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 1px 8px;\n border-radius: 999px;\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 600;\n}\n.cfi-vi .cfi-vi-tag--default {\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--outline {\n background: transparent;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--secondary {\n background: var(--cfi-color-gray-200, #e5e7eb);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--success {\n background: #16a34a;\n color: #fff;\n}\n.cfi-vi .cfi-vi-tag--purple {\n background: var(--cfi-color-violet-600, #7c3aed);\n color: #fff;\n}\n.cfi-vi .cfi-vi-card {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-lg, 8px);\n background: #fff;\n}\n.cfi-vi .cfi-vi-card--active {\n border-color: #86efac;\n background: #f0fdf4;\n}\n.cfi-vi .cfi-vi-card--hover:hover {\n border-color: var(--cfi-color-primary-600, #2563eb);\n}\n.cfi-vi .cfi-vi-card-head {\n padding: var(--cfi-spacing-md, 12px);\n padding-bottom: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi .cfi-vi-card-body {\n padding: 0 var(--cfi-spacing-md, 12px) var(--cfi-spacing-md, 12px);\n}\n.cfi-vi .cfi-vi-card-title {\n font-size: var(--cfi-font-size-base, 15px);\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.cfi-vi .cfi-vi-link {\n background: none;\n border: none;\n padding: 0;\n color: inherit;\n font: inherit;\n cursor: pointer;\n text-align: left;\n}\n.cfi-vi .cfi-vi-link:hover {\n text-decoration: underline;\n}\n.cfi-vi .cfi-vi-field {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 4px);\n}\n.cfi-vi .cfi-vi-label {\n font-size: var(--cfi-font-size-sm, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-hint {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-gray-500, #6b7280);\n margin: 2px 0 0;\n}\n.cfi-vi .cfi-vi-input,\n.cfi-vi select.cfi-vi-input {\n padding: 6px 8px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n font: inherit;\n color: inherit;\n width: 100%;\n}\n.cfi-vi .cfi-vi-input:focus {\n outline: 2px solid var(--cfi-color-primary-600, #2563eb);\n outline-offset: 1px;\n}\n.cfi-vi-modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n padding: 16px;\n}\n.cfi-vi-modal {\n background: #fff;\n border-radius: var(--cfi-radius-lg, 8px);\n width: 100%;\n max-width: 32rem;\n max-height: 90vh;\n overflow: auto;\n box-shadow: var(--cfi-shadow-lg, 0 10px 30px rgba(0,0,0,0.2));\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n}\n.cfi-vi-modal-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-md, 14px);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-vi-modal-title {\n font-size: var(--cfi-font-size-lg, 16px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi-modal-body {\n padding: var(--cfi-spacing-md, 14px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n}\n.cfi-vi-modal-foot {\n display: flex;\n justify-content: flex-end;\n gap: var(--cfi-spacing-sm, 8px);\n padding: var(--cfi-spacing-md, 14px);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-vi-icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--cfi-color-gray-500, #6b7280);\n border-radius: 4px;\n display: inline-flex;\n}\n.cfi-vi .cfi-vi-spinner {\n display: inline-block;\n border: 2px solid var(--cfi-color-gray-300, #d1d5db);\n border-top-color: var(--cfi-color-primary-600, #2563eb);\n border-radius: 50%;\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n.cfi-vi .cfi-vi-list {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 10px);\n}\n.cfi-vi .cfi-vi-list-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n}\n.cfi-vi .cfi-vi-h1 {\n font-size: var(--cfi-font-size-lg, 18px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi .cfi-vi-active-line {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--cfi-font-size-sm, 13px);\n color: var(--cfi-color-gray-600, #4b5563);\n margin-top: 2px;\n}\n.cfi-vi .cfi-vi-active-name {\n font-weight: 600;\n color: #15803d;\n}\n.cfi-vi .cfi-vi-active-extras {\n margin-top: var(--cfi-spacing-md, 16px);\n padding-top: var(--cfi-spacing-md, 16px);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 16px);\n}\n.cfi-vi-mapper {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--cfi-spacing-lg, 24px);\n align-items: start;\n}\n@media (min-width: 1024px) {\n .cfi-vi-mapper {\n grid-template-columns: 1fr 1fr;\n }\n}\n.cfi-vi-mapper .cfi-vi-mapper-col {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-lg, 24px);\n min-width: 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-section {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi-mapper .cfi-vi-mapper-section-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-h3 {\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 700;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--cfi-color-gray-500, #6b7280);\n margin: 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-note {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi-mapper .cfi-vi-mapper-box {\n border: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n border-radius: var(--cfi-radius-lg, 8px);\n background: #fff;\n padding: var(--cfi-spacing-md, 14px);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-err {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-error, #b91c1c);\n margin: 2px 0 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-save {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-top: 4px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-dirty {\n font-size: var(--cfi-font-size-xs, 11px);\n color: #b45309;\n}\n.cfi-vi-mapper .cfi-vi-hier-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-hier-elbow {\n width: 18px;\n align-self: stretch;\n border-left: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n height: 18px;\n margin-bottom: 9px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-hier-dot {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n border: 2px solid var(--cfi-color-gray-300, #d1d5db);\n background: #fff;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-hier-dot--filled {\n border-color: var(--cfi-color-primary-600, #6366f1);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-hier-name {\n font-size: var(--cfi-font-size-xs, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-600, #4b5563);\n width: 96px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-leaf {\n font-size: 10px;\n font-weight: 600;\n color: #b45309;\n background: #fffbeb;\n border: 1px solid #fde68a;\n padding: 1px 6px;\n border-radius: 999px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-dropzone {\n flex: 1;\n min-height: 36px;\n border-radius: var(--cfi-radius-md, 6px);\n border: 2px solid;\n display: flex;\n align-items: center;\n padding: 0 10px;\n min-width: 0;\n transition: all 0.12s;\n}\n.cfi-vi-mapper .cfi-vi-dropzone--active {\n border-color: var(--cfi-color-primary-600, #6366f1);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-dropzone--filled {\n border-style: solid;\n border-color: var(--cfi-color-gray-200, #e5e7eb);\n background: #fff;\n}\n.cfi-vi-mapper .cfi-vi-dropzone--empty {\n border-style: dashed;\n border-color: var(--cfi-color-gray-200, #e5e7eb);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-vi-mapper .cfi-vi-dropzone--covered {\n border-style: solid;\n border-color: #a7f3d0;\n background: #ecfdf5;\n}\n.cfi-vi-mapper .cfi-vi-dropzone-ph {\n font-size: var(--cfi-font-size-xs, 12px);\n color: var(--cfi-color-gray-400, #9ca3af);\n user-select: none;\n}\n.cfi-vi-mapper .cfi-vi-base-hint {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n color: #1d4ed8;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n padding: 1px 6px;\n border-radius: 4px;\n flex-shrink: 0;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-auto-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-auto-label {\n width: 160px;\n flex-shrink: 0;\n padding-top: 6px;\n}\n.cfi-vi-mapper .cfi-vi-auto-label-text {\n font-size: var(--cfi-font-size-xs, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi-mapper .cfi-vi-auto-cover {\n font-size: 10px;\n margin: 2px 0 0;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi-mapper .cfi-vi-auto-cover--on {\n color: #047857;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-width: 0;\n color: #047857;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto-label {\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto-tag {\n font-size: 10px;\n font-style: italic;\n color: #059669;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-auto-transform {\n width: 224px;\n flex-shrink: 0;\n padding-top: 6px;\n font-size: 11px;\n color: var(--cfi-color-gray-500, #6b7280);\n line-height: 1.3;\n}\n.cfi-vi-mapper .cfi-vi-auto-transform-label {\n font-weight: 600;\n}\n.cfi-vi-mapper .cfi-vi-pool-col {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n@media (min-width: 1024px) {\n .cfi-vi-mapper .cfi-vi-pool-col {\n position: sticky;\n top: 16px;\n align-self: start;\n }\n}\n.cfi-vi-mapper .cfi-vi-search {\n position: relative;\n}\n.cfi-vi-mapper .cfi-vi-search .cfi-vi-input {\n padding-left: 28px;\n}\n.cfi-vi-mapper .cfi-vi-search-icon {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--cfi-color-gray-400, #9ca3af);\n pointer-events: none;\n}\n.cfi-vi-mapper .cfi-vi-pool {\n border: 2px dashed var(--cfi-color-gray-200, #e5e7eb);\n background: var(--cfi-color-gray-50, #f9fafb);\n border-radius: var(--cfi-radius-lg, 8px);\n padding: 12px;\n min-height: 200px;\n max-height: 600px;\n overflow-y: auto;\n}\n.cfi-vi-mapper .cfi-vi-pool--active {\n border-color: var(--cfi-color-primary-600, #818cf8);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-pool-empty {\n font-size: var(--cfi-font-size-xs, 12px);\n color: var(--cfi-color-gray-400, #9ca3af);\n font-style: italic;\n}\n.cfi-vi-mapper .cfi-vi-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.cfi-vi-mapper .cfi-vi-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n cursor: grab;\n user-select: none;\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi-mapper .cfi-vi-chip:hover {\n border-color: var(--cfi-color-primary-600, #818cf8);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-chip:active {\n cursor: grabbing;\n}\n.cfi-vi-mapper .cfi-vi-chip--assigned {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-width: 0;\n cursor: grab;\n}\n.cfi-vi-mapper .cfi-vi-chip--assigned:active {\n cursor: grabbing;\n}\n.cfi-vi-mapper .cfi-vi-chip-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--cfi-color-gray-400, #9ca3af);\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-chip-dot--on {\n background: var(--cfi-color-primary-600, #6366f1);\n}\n.cfi-vi-mapper .cfi-vi-chip-label {\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n color: #3730a3;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-chip-x {\n margin-left: 4px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--cfi-color-gray-400, #9ca3af);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-chip-x:hover {\n background: #fee2e2;\n color: #ef4444;\n}\n.cfi-vi .cfi-vi-spin {\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n.cfi-vi .cfi-vi-detail {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-lg, 20px);\n}\n.cfi-vi .cfi-vi-h2 {\n font-size: var(--cfi-font-size-base, 15px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi .cfi-vi-panel {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n padding: var(--cfi-spacing-md, 16px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-grid2 {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--cfi-spacing-md, 16px);\n}\n@media (min-width: 768px) {\n .cfi-vi .cfi-vi-grid2 {\n grid-template-columns: 1fr 1fr;\n }\n}\n.cfi-vi .cfi-vi-lock {\n display: flex;\n align-items: center;\n gap: 8px;\n border: 1px solid #fde68a;\n background: #fffbeb;\n color: #92400e;\n border-radius: var(--cfi-radius-md, 6px);\n padding: 8px 12px;\n font-size: var(--cfi-font-size-xs, 12px);\n}\n.cfi-vi .cfi-vi-table {\n width: 100%;\n border-collapse: collapse;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-table th,\n.cfi-vi .cfi-vi-table td {\n text-align: left;\n padding: 6px 10px;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n vertical-align: middle;\n}\n.cfi-vi .cfi-vi-table th {\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi .cfi-vi-table tr:last-child td {\n border-bottom: none;\n}\n.cfi-vi .cfi-vi-danger {\n border: 1px solid rgba(220, 38, 38, 0.4);\n background: rgba(220, 38, 38, 0.05);\n border-radius: var(--cfi-radius-md, 6px);\n padding: var(--cfi-spacing-md, 16px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-aliases {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-alias-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n");
28
+ styleInject(".cfi-volume-import {\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-base, 14px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n max-width: 640px;\n}\n.cfi-volume-import .cfi-vi-field {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 4px);\n}\n.cfi-volume-import .cfi-vi-row {\n display: flex;\n gap: var(--cfi-spacing-md, 12px);\n flex-wrap: wrap;\n}\n.cfi-volume-import .cfi-vi-row .cfi-vi-field {\n flex: 1 1 8rem;\n}\n.cfi-volume-import .cfi-vi-label {\n font-size: var(--cfi-font-size-sm, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-volume-import .cfi-vi-select,\n.cfi-volume-import .cfi-vi-file {\n padding: 6px 8px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n font: inherit;\n color: inherit;\n}\n.cfi-volume-import .cfi-vi-select:focus,\n.cfi-volume-import .cfi-vi-file:focus {\n outline: 2px solid var(--cfi-color-primary-600, #2563eb);\n outline-offset: 1px;\n}\n.cfi-volume-import .cfi-vi-button {\n display: inline-flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 6px);\n align-self: flex-start;\n padding: 8px 14px;\n border: none;\n border-radius: var(--cfi-radius-md, 6px);\n background: var(--cfi-color-primary-600, #2563eb);\n color: #fff;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n.cfi-volume-import .cfi-vi-button:hover:not(:disabled) {\n background: var(--cfi-color-primary-700, #1d4ed8);\n}\n.cfi-volume-import .cfi-vi-button:disabled {\n background: var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-500, #6b7280);\n cursor: not-allowed;\n}\n.cfi-volume-import .cfi-vi-loading,\n.cfi-volume-import .cfi-vi-alert {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 6px);\n padding: 8px 10px;\n border-radius: var(--cfi-radius-md, 6px);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-alert--error {\n background: var(--cfi-color-gray-100, #fef2f2);\n color: var(--cfi-color-error, #b91c1c);\n}\n.cfi-volume-import .cfi-vi-alert--warn {\n background: #fffbeb;\n color: #92400e;\n align-items: flex-start;\n}\n.cfi-volume-import .cfi-vi-blockers {\n margin: 0;\n padding-left: 1.1rem;\n}\n.cfi-volume-import .cfi-vi-status {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n padding: var(--cfi-spacing-md, 12px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-volume-import .cfi-vi-status-head {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-volume-import .cfi-vi-filename {\n font-weight: 600;\n}\n.cfi-volume-import .cfi-vi-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: var(--cfi-font-size-xs, 11px);\n text-transform: capitalize;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-volume-import .cfi-vi-badge--done {\n background: #dcfce7;\n color: #166534;\n}\n.cfi-volume-import .cfi-vi-badge--done_with_errors {\n background: #fef9c3;\n color: #854d0e;\n}\n.cfi-volume-import .cfi-vi-badge--failed {\n background: #fee2e2;\n color: #991b1b;\n}\n.cfi-volume-import .cfi-vi-badge--processing,\n.cfi-volume-import .cfi-vi-badge--pending,\n.cfi-volume-import .cfi-vi-badge--awaiting_calendar {\n background: #dbeafe;\n color: #1e40af;\n}\n.cfi-volume-import .cfi-vi-link {\n background: none;\n border: none;\n padding: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--cfi-color-primary-600, #2563eb);\n font: inherit;\n cursor: pointer;\n}\n.cfi-volume-import .cfi-vi-stats {\n display: flex;\n gap: var(--cfi-spacing-md, 12px);\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-errors {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-volume-import .cfi-vi-errors th,\n.cfi-volume-import .cfi-vi-errors td {\n text-align: left;\n padding: 4px 8px;\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n vertical-align: top;\n}\n.cfi-volume-import .cfi-vi-spin {\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n@keyframes cfi-vi-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.cfi-vi {\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-base, 14px);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n}\n.cfi-vi *,\n.cfi-vi *::before,\n.cfi-vi *::after {\n box-sizing: border-box;\n}\n.cfi-vi .cfi-vi-row {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi .cfi-vi-row--between {\n justify-content: space-between;\n}\n.cfi-vi .cfi-vi-spacer {\n flex: 1;\n}\n.cfi-vi .cfi-vi-muted {\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-vi .cfi-vi-error {\n color: var(--cfi-color-error, #b91c1c);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-mono {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n font-size: var(--cfi-font-size-xs, 11px);\n}\n.cfi-vi .cfi-vi-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border-radius: var(--cfi-radius-md, 6px);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n.cfi-vi .cfi-vi-btn--sm {\n padding: 4px 10px;\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-btn--md {\n padding: 8px 14px;\n}\n.cfi-vi .cfi-vi-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-vi .cfi-vi-btn--primary {\n background: var(--cfi-color-primary-600, #2563eb);\n color: #fff;\n}\n.cfi-vi .cfi-vi-btn--primary:hover:not(:disabled) {\n background: var(--cfi-color-primary-700, #1d4ed8);\n}\n.cfi-vi .cfi-vi-btn--outline {\n background: #fff;\n border-color: var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-vi .cfi-vi-btn--outline:hover:not(:disabled) {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-vi .cfi-vi-btn--ghost {\n background: transparent;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-btn--ghost:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-vi .cfi-vi-btn--destructive {\n background: var(--cfi-color-error, #dc2626);\n color: #fff;\n}\n.cfi-vi .cfi-vi-btn--destructive:hover:not(:disabled) {\n filter: brightness(0.92);\n}\n.cfi-vi .cfi-vi-icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--cfi-color-gray-500, #6b7280);\n border-radius: var(--cfi-radius-sm, 4px);\n display: inline-flex;\n}\n.cfi-vi .cfi-vi-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-vi .cfi-vi-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 1px 8px;\n border-radius: 999px;\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 600;\n}\n.cfi-vi .cfi-vi-tag--default {\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--outline {\n background: transparent;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--secondary {\n background: var(--cfi-color-gray-200, #e5e7eb);\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-tag--success {\n background: #16a34a;\n color: #fff;\n}\n.cfi-vi .cfi-vi-tag--purple {\n background: var(--cfi-color-violet-600, #7c3aed);\n color: #fff;\n}\n.cfi-vi .cfi-vi-card {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-lg, 8px);\n background: #fff;\n}\n.cfi-vi .cfi-vi-card--active {\n border-color: #86efac;\n background: #f0fdf4;\n}\n.cfi-vi .cfi-vi-card--hover:hover {\n border-color: var(--cfi-color-primary-600, #2563eb);\n}\n.cfi-vi .cfi-vi-card-head {\n padding: var(--cfi-spacing-md, 12px);\n padding-bottom: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi .cfi-vi-card-body {\n padding: 0 var(--cfi-spacing-md, 12px) var(--cfi-spacing-md, 12px);\n}\n.cfi-vi .cfi-vi-card-title {\n font-size: var(--cfi-font-size-base, 15px);\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.cfi-vi .cfi-vi-link {\n background: none;\n border: none;\n padding: 0;\n color: inherit;\n font: inherit;\n cursor: pointer;\n text-align: left;\n}\n.cfi-vi .cfi-vi-link:hover {\n text-decoration: underline;\n}\n.cfi-vi .cfi-vi-field {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-xs, 4px);\n}\n.cfi-vi .cfi-vi-label {\n font-size: var(--cfi-font-size-sm, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi .cfi-vi-hint {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-gray-500, #6b7280);\n margin: 2px 0 0;\n}\n.cfi-vi .cfi-vi-input,\n.cfi-vi select.cfi-vi-input {\n padding: 6px 8px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n font: inherit;\n color: inherit;\n width: 100%;\n}\n.cfi-vi .cfi-vi-input:focus {\n outline: 2px solid var(--cfi-color-primary-600, #2563eb);\n outline-offset: 1px;\n}\n.cfi-vi-modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 50;\n padding: 16px;\n}\n.cfi-vi-modal {\n background: #fff;\n border-radius: var(--cfi-radius-lg, 8px);\n width: 100%;\n max-width: 32rem;\n max-height: 90vh;\n overflow: auto;\n box-shadow: var(--cfi-shadow-lg, 0 10px 30px rgba(0,0,0,0.2));\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n color: var(--cfi-color-gray-900, #111827);\n display: flex;\n flex-direction: column;\n}\n.cfi-vi-modal-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-md, 14px);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-vi-modal-title {\n font-size: var(--cfi-font-size-lg, 16px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi-modal-body {\n padding: var(--cfi-spacing-md, 14px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 12px);\n}\n.cfi-vi-modal-foot {\n display: flex;\n justify-content: flex-end;\n gap: var(--cfi-spacing-sm, 8px);\n padding: var(--cfi-spacing-md, 14px);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-vi-icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px;\n color: var(--cfi-color-gray-500, #6b7280);\n border-radius: 4px;\n display: inline-flex;\n}\n.cfi-vi .cfi-vi-spinner {\n display: inline-block;\n border: 2px solid var(--cfi-color-gray-300, #d1d5db);\n border-top-color: var(--cfi-color-primary-600, #2563eb);\n border-radius: 50%;\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n.cfi-vi .cfi-vi-list {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 10px);\n}\n.cfi-vi .cfi-vi-list-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n}\n.cfi-vi .cfi-vi-h1 {\n font-size: var(--cfi-font-size-lg, 18px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi .cfi-vi-active-line {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--cfi-font-size-sm, 13px);\n color: var(--cfi-color-gray-600, #4b5563);\n margin-top: 2px;\n}\n.cfi-vi .cfi-vi-active-name {\n font-weight: 600;\n color: #15803d;\n}\n.cfi-vi .cfi-vi-active-extras {\n margin-top: var(--cfi-spacing-md, 16px);\n padding-top: var(--cfi-spacing-md, 16px);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 16px);\n}\n.cfi-vi-mapper {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--cfi-spacing-lg, 24px);\n align-items: start;\n}\n@media (min-width: 1024px) {\n .cfi-vi-mapper {\n grid-template-columns: 1fr 1fr;\n }\n}\n.cfi-vi-mapper .cfi-vi-mapper-col {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-lg, 24px);\n min-width: 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-section {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n.cfi-vi-mapper .cfi-vi-mapper-section-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-h3 {\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 700;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--cfi-color-gray-500, #6b7280);\n margin: 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-note {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi-mapper .cfi-vi-mapper-box {\n border: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n border-radius: var(--cfi-radius-lg, 8px);\n background: #fff;\n padding: var(--cfi-spacing-md, 14px);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-err {\n font-size: var(--cfi-font-size-xs, 11px);\n color: var(--cfi-color-error, #b91c1c);\n margin: 2px 0 0;\n}\n.cfi-vi-mapper .cfi-vi-mapper-save {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-top: 4px;\n}\n.cfi-vi-mapper .cfi-vi-mapper-dirty {\n font-size: var(--cfi-font-size-xs, 11px);\n color: #b45309;\n}\n.cfi-vi-mapper .cfi-vi-hier-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-hier-elbow {\n width: 18px;\n align-self: stretch;\n border-left: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n height: 18px;\n margin-bottom: 9px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-hier-dot {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n border: 2px solid var(--cfi-color-gray-300, #d1d5db);\n background: #fff;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-hier-dot--filled {\n border-color: var(--cfi-color-primary-600, #6366f1);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-hier-name {\n font-size: var(--cfi-font-size-xs, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-600, #4b5563);\n width: 96px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-leaf {\n font-size: 10px;\n font-weight: 600;\n color: #b45309;\n background: #fffbeb;\n border: 1px solid #fde68a;\n padding: 1px 6px;\n border-radius: 999px;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-dropzone {\n flex: 1;\n min-height: 36px;\n border-radius: var(--cfi-radius-md, 6px);\n border: 2px solid;\n display: flex;\n align-items: center;\n padding: 0 10px;\n min-width: 0;\n transition: all 0.12s;\n}\n.cfi-vi-mapper .cfi-vi-dropzone--active {\n border-color: var(--cfi-color-primary-600, #6366f1);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-dropzone--filled {\n border-style: solid;\n border-color: var(--cfi-color-gray-200, #e5e7eb);\n background: #fff;\n}\n.cfi-vi-mapper .cfi-vi-dropzone--empty {\n border-style: dashed;\n border-color: var(--cfi-color-gray-200, #e5e7eb);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-vi-mapper .cfi-vi-dropzone--covered {\n border-style: solid;\n border-color: #a7f3d0;\n background: #ecfdf5;\n}\n.cfi-vi-mapper .cfi-vi-dropzone-ph {\n font-size: var(--cfi-font-size-xs, 12px);\n color: var(--cfi-color-gray-400, #9ca3af);\n user-select: none;\n}\n.cfi-vi-mapper .cfi-vi-base-hint {\n font-size: 10px;\n font-family: ui-monospace, monospace;\n color: #1d4ed8;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n padding: 1px 6px;\n border-radius: 4px;\n flex-shrink: 0;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-auto-row {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n.cfi-vi-mapper .cfi-vi-auto-label {\n width: 160px;\n flex-shrink: 0;\n padding-top: 6px;\n}\n.cfi-vi-mapper .cfi-vi-auto-label-text {\n font-size: var(--cfi-font-size-xs, 12px);\n font-weight: 600;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi-mapper .cfi-vi-auto-cover {\n font-size: 10px;\n margin: 2px 0 0;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi-mapper .cfi-vi-auto-cover--on {\n color: #047857;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-width: 0;\n color: #047857;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto-label {\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-auto-auto-tag {\n font-size: 10px;\n font-style: italic;\n color: #059669;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-auto-transform {\n width: 224px;\n flex-shrink: 0;\n padding-top: 6px;\n font-size: 11px;\n color: var(--cfi-color-gray-500, #6b7280);\n line-height: 1.3;\n}\n.cfi-vi-mapper .cfi-vi-auto-transform-label {\n font-weight: 600;\n}\n.cfi-vi-mapper .cfi-vi-pool-col {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 8px);\n}\n@media (min-width: 1024px) {\n .cfi-vi-mapper .cfi-vi-pool-col {\n position: sticky;\n top: 16px;\n align-self: start;\n }\n}\n.cfi-vi-mapper .cfi-vi-search {\n position: relative;\n}\n.cfi-vi-mapper .cfi-vi-search .cfi-vi-input {\n padding-left: 28px;\n}\n.cfi-vi-mapper .cfi-vi-search-icon {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--cfi-color-gray-400, #9ca3af);\n pointer-events: none;\n}\n.cfi-vi-mapper .cfi-vi-pool {\n border: 2px dashed var(--cfi-color-gray-200, #e5e7eb);\n background: var(--cfi-color-gray-50, #f9fafb);\n border-radius: var(--cfi-radius-lg, 8px);\n padding: 12px;\n min-height: 200px;\n max-height: 600px;\n overflow-y: auto;\n}\n.cfi-vi-mapper .cfi-vi-pool--active {\n border-color: var(--cfi-color-primary-600, #818cf8);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-pool-empty {\n font-size: var(--cfi-font-size-xs, 12px);\n color: var(--cfi-color-gray-400, #9ca3af);\n font-style: italic;\n}\n.cfi-vi-mapper .cfi-vi-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.cfi-vi-mapper .cfi-vi-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n cursor: grab;\n user-select: none;\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n color: var(--cfi-color-gray-700, #374151);\n}\n.cfi-vi-mapper .cfi-vi-chip:hover {\n border-color: var(--cfi-color-primary-600, #818cf8);\n background: var(--cfi-color-primary-50, #eef2ff);\n}\n.cfi-vi-mapper .cfi-vi-chip:active {\n cursor: grabbing;\n}\n.cfi-vi-mapper .cfi-vi-chip--assigned {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-width: 0;\n cursor: grab;\n}\n.cfi-vi-mapper .cfi-vi-chip--assigned:active {\n cursor: grabbing;\n}\n.cfi-vi-mapper .cfi-vi-chip-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--cfi-color-gray-400, #9ca3af);\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-chip-dot--on {\n background: var(--cfi-color-primary-600, #6366f1);\n}\n.cfi-vi-mapper .cfi-vi-chip-label {\n font-size: var(--cfi-font-size-xs, 12px);\n font-family: ui-monospace, monospace;\n color: #3730a3;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-vi-mapper .cfi-vi-chip-x {\n margin-left: 4px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n border: none;\n background: none;\n cursor: pointer;\n color: var(--cfi-color-gray-400, #9ca3af);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.cfi-vi-mapper .cfi-vi-chip-x:hover {\n background: #fee2e2;\n color: #ef4444;\n}\n.cfi-vi .cfi-vi-spin {\n animation: cfi-vi-spin 0.8s linear infinite;\n}\n.cfi-vi .cfi-vi-detail {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-lg, 20px);\n}\n.cfi-vi .cfi-vi-h2 {\n font-size: var(--cfi-font-size-base, 15px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi .cfi-vi-panel {\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n padding: var(--cfi-spacing-md, 16px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-grid2 {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--cfi-spacing-md, 16px);\n}\n@media (min-width: 768px) {\n .cfi-vi .cfi-vi-grid2 {\n grid-template-columns: 1fr 1fr;\n }\n}\n.cfi-vi .cfi-vi-lock {\n display: flex;\n align-items: center;\n gap: 8px;\n border: 1px solid #fde68a;\n background: #fffbeb;\n color: #92400e;\n border-radius: var(--cfi-radius-md, 6px);\n padding: 8px 12px;\n font-size: var(--cfi-font-size-xs, 12px);\n}\n.cfi-vi .cfi-vi-table {\n width: 100%;\n border-collapse: collapse;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 6px);\n font-size: var(--cfi-font-size-sm, 13px);\n}\n.cfi-vi .cfi-vi-table th,\n.cfi-vi .cfi-vi-table td {\n text-align: left;\n padding: 6px 10px;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n vertical-align: middle;\n}\n.cfi-vi .cfi-vi-table th {\n font-size: var(--cfi-font-size-xs, 11px);\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-vi .cfi-vi-table tr:last-child td {\n border-bottom: none;\n}\n.cfi-vi .cfi-vi-danger {\n border: 1px solid rgba(220, 38, 38, 0.4);\n background: rgba(220, 38, 38, 0.05);\n border-radius: var(--cfi-radius-md, 6px);\n padding: var(--cfi-spacing-md, 16px);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-aliases {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-sm, 12px);\n}\n.cfi-vi .cfi-vi-alias-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.cfi-vi .cfi-vi-tabs {\n display: flex;\n flex-wrap: wrap;\n gap: 2px;\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-vi .cfi-vi-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n margin-bottom: -1px;\n border: none;\n border-bottom: 2px solid transparent;\n background: none;\n font: inherit;\n font-size: var(--cfi-font-size-sm, 13px);\n cursor: pointer;\n color: var(--cfi-color-gray-600, #4b5563);\n}\n.cfi-vi .cfi-vi-tab:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-vi .cfi-vi-tab--active {\n color: var(--cfi-color-primary-600, #2563eb);\n border-bottom-color: var(--cfi-color-primary-600, #2563eb);\n font-weight: 600;\n}\n.cfi-vi .cfi-vi-tab--danger:hover {\n color: #dc2626;\n}\n.cfi-vi .cfi-vi-tab--danger.cfi-vi-tab--active {\n color: #dc2626;\n border-bottom-color: #dc2626;\n}\n.cfi-vi .cfi-vi-tabpanel {\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-lg, 20px);\n}\n.cfi-vi .cfi-vi-tag--warn {\n background: #fffbeb;\n color: #92400e;\n border: 1px solid #fde68a;\n}\n.cfi-vi .cfi-vi-tag--danger {\n background: #fef2f2;\n color: #b91c1c;\n border: 1px solid #fecaca;\n}\n.cfi-vi .cfi-vi-select {\n padding: 6px 8px;\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n border-radius: var(--cfi-radius-md, 6px);\n background: #fff;\n font: inherit;\n}\n.cfi-vi .cfi-vi-select:focus {\n outline: 2px solid var(--cfi-color-primary-600, #2563eb);\n outline-offset: -1px;\n}\n.cfi-vi .cfi-vi-file {\n font: inherit;\n}\n.cfi-vi .cfi-vi-alert {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: var(--cfi-radius-md, 6px);\n font-size: var(--cfi-font-size-sm, 13px);\n margin: 8px 0;\n}\n.cfi-vi .cfi-vi-alert--error {\n background: #fef2f2;\n color: #b91c1c;\n}\n.cfi-vi .cfi-vi-alert--warn {\n background: #fffbeb;\n color: #92400e;\n}\n.cfi-vi .cfi-vi-alert ul.cfi-vi-blockers {\n margin: 0;\n padding-left: 18px;\n}\n.cfi-vi .cfi-vi-loading {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 12px 0;\n}\n.cfi-vi .cfi-vi-files-head {\n align-items: center;\n margin-bottom: 12px;\n}\n.cfi-vi .cfi-vi-files-title {\n font-size: var(--cfi-font-size-lg, 18px);\n font-weight: 600;\n margin: 0;\n}\n.cfi-vi .cfi-vi-files-filters {\n gap: 8px;\n align-items: center;\n margin-bottom: 12px;\n}\n.cfi-vi .cfi-vi-files-table th,\n.cfi-vi .cfi-vi-files-table td {\n white-space: nowrap;\n}\n.cfi-vi .cfi-vi-files-file {\n font-family: var(--cfi-font-mono, ui-monospace, monospace);\n max-width: 220px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.cfi-vi .cfi-vi-files-actions {\n display: flex;\n gap: 4px;\n justify-content: flex-end;\n}\n.cfi-vi .cfi-vi-files-detail td {\n background: var(--cfi-color-gray-50, #f9fafb);\n white-space: normal;\n}\n.cfi-vi .cfi-vi-files-pager {\n align-items: center;\n gap: 10px;\n margin-top: 12px;\n}\n.cfi-vi .cfi-vi-ta-right {\n text-align: right;\n}\n.cfi-vi .cfi-vi-danger-text {\n color: #b91c1c;\n}\n");
29
29
 
30
30
  // src/VolumeImport.tsx
31
31
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -271,9 +271,9 @@ function UploadStatusPanel(props) {
271
271
  ] });
272
272
  }
273
273
 
274
- // src/VolumeSourcesList.tsx
275
- import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
276
- import { CheckCircle2, Sparkles } from "lucide-react";
274
+ // src/VolumeFiles.tsx
275
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
276
+ import { AlertTriangle as AlertTriangle2, Download, RotateCcw as RotateCcw2, FileUp as FileUp2, ChevronRight, ChevronDown } from "lucide-react";
277
277
 
278
278
  // src/ui.tsx
279
279
  import { X } from "lucide-react";
@@ -310,6 +310,9 @@ function TextInput(props) {
310
310
  const { className, ...rest } = props;
311
311
  return /* @__PURE__ */ jsx2("input", { className: className ? `cfi-vi-input ${className}` : "cfi-vi-input", ...rest });
312
312
  }
313
+ function Spinner(props) {
314
+ return /* @__PURE__ */ jsx2("span", { className: "cfi-vi-spinner", style: { width: props.size ?? 14, height: props.size ?? 14 }, "aria-hidden": true });
315
+ }
313
316
  function Modal(props) {
314
317
  if (!props.open) return null;
315
318
  return /* @__PURE__ */ jsx2("div", { className: "cfi-vi-modal-overlay", role: "dialog", "aria-modal": "true", onClick: props.onClose, children: /* @__PURE__ */ jsxs2("div", { className: "cfi-vi-modal", onClick: (e) => e.stopPropagation(), children: [
@@ -322,23 +325,469 @@ function Modal(props) {
322
325
  ] }) });
323
326
  }
324
327
 
325
- // src/VolumeSourcesList.tsx
328
+ // src/VolumeFiles.tsx
326
329
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
327
- var EMPTY_FORM = { code: "", name: "", description: "", headerRowIndex: 0, volumeHeaderFormat: "MMM yyyy" };
328
- function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras, className }) {
330
+ var MONTH_NAMES2 = [
331
+ "January",
332
+ "February",
333
+ "March",
334
+ "April",
335
+ "May",
336
+ "June",
337
+ "July",
338
+ "August",
339
+ "September",
340
+ "October",
341
+ "November",
342
+ "December"
343
+ ];
344
+ var MONTH_ABBR = MONTH_NAMES2.map((m) => m.slice(0, 3));
345
+ var CURRENT_YEAR = (/* @__PURE__ */ new Date()).getFullYear();
346
+ var CURRENT_MONTH = (/* @__PURE__ */ new Date()).getMonth() + 1;
347
+ var YEAR_OPTIONS = [CURRENT_YEAR - 2, CURRENT_YEAR - 1, CURRENT_YEAR, CURRENT_YEAR + 1, CURRENT_YEAR + 2];
348
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "processing", "awaiting_calendar"]);
349
+ var POLL_INTERVAL_MS = 3e3;
350
+ var PAGE_SIZE = 20;
351
+ var STATUS_LABELS2 = {
352
+ awaiting_calendar: "awaiting calendar",
353
+ done_with_errors: "done with errors"
354
+ };
355
+ function statusTagClass(status) {
356
+ switch (status) {
357
+ case "done":
358
+ return "cfi-vi-tag--success";
359
+ case "done_with_errors":
360
+ return "cfi-vi-tag--warn";
361
+ case "awaiting_calendar":
362
+ return "cfi-vi-tag--warn";
363
+ case "failed":
364
+ return "cfi-vi-tag--danger";
365
+ case "processing":
366
+ return "cfi-vi-tag--secondary";
367
+ default:
368
+ return "cfi-vi-tag--default";
369
+ }
370
+ }
371
+ function computeCutoffLabel(uploadYear, uploadMonth, lag) {
372
+ let cutoffMonth = uploadMonth - lag;
373
+ let cutoffYear = uploadYear;
374
+ while (cutoffMonth <= 0) {
375
+ cutoffMonth += 12;
376
+ cutoffYear--;
377
+ }
378
+ let predMonth = cutoffMonth + 1;
379
+ let predYear = cutoffYear;
380
+ if (predMonth > 12) {
381
+ predMonth -= 12;
382
+ predYear++;
383
+ }
384
+ return {
385
+ cutoffLabel: `${MONTH_NAMES2[cutoffMonth - 1]} ${cutoffYear}`,
386
+ predictionsLabel: `${MONTH_NAMES2[predMonth - 1]} ${predYear}`
387
+ };
388
+ }
389
+ function formatDuration(startedAt, completedAt) {
390
+ if (!startedAt || !completedAt) return "\u2014";
391
+ const ms = new Date(completedAt).getTime() - new Date(startedAt).getTime();
392
+ const totalSec = Math.max(0, Math.floor(ms / 1e3));
393
+ const m = Math.floor(totalSec / 60);
394
+ const s = totalSec % 60;
395
+ return m > 0 ? `${m}m ${s}s` : `${s}s`;
396
+ }
397
+ function UploadDialog(props) {
398
+ const { transport, sources, activeSourceId, calendarSeeded, onClose, onCreated } = props;
399
+ const [selectedSourceId, setSelectedSourceId] = useState2(activeSourceId ?? sources[0]?.id ?? "");
400
+ const [year, setYear] = useState2(CURRENT_YEAR);
401
+ const [month, setMonth] = useState2(CURRENT_MONTH);
402
+ const [lag, setLag] = useState2(1);
403
+ const [file, setFile] = useState2(null);
404
+ const [submitting, setSubmitting] = useState2(false);
405
+ const [error, setError] = useState2(null);
406
+ const [hierarchyMapped, setHierarchyMapped] = useState2(null);
407
+ const selected = sources.find((s) => s.id === selectedSourceId) ?? null;
408
+ const isTall = selected?.layout === "tall";
409
+ useEffect2(() => {
410
+ if (!selectedSourceId) {
411
+ setHierarchyMapped(null);
412
+ return;
413
+ }
414
+ let cancelled = false;
415
+ setHierarchyMapped(null);
416
+ transport.isHierarchyMapped(selectedSourceId).then((m) => {
417
+ if (!cancelled) setHierarchyMapped(m);
418
+ }).catch(() => {
419
+ if (!cancelled) setHierarchyMapped(false);
420
+ });
421
+ return () => {
422
+ cancelled = true;
423
+ };
424
+ }, [selectedSourceId, transport]);
425
+ const blockers = [];
426
+ if (!selected) blockers.push("Pick a source to upload to");
427
+ else {
428
+ if (!isTall && !selected.uniqueKeySegmentId) blockers.push("Source has no unique-key column set");
429
+ if (hierarchyMapped === false) blockers.push("Hierarchy levels not mapped");
430
+ }
431
+ if (calendarSeeded === false) blockers.push("Calendar not seeded");
432
+ const blocked = blockers.length > 0;
433
+ const { cutoffLabel, predictionsLabel } = computeCutoffLabel(year, month, lag);
434
+ const handleSubmit = useCallback2(async () => {
435
+ if (!selected || !file) {
436
+ setError("Pick a source and file first");
437
+ return;
438
+ }
439
+ setSubmitting(true);
440
+ setError(null);
441
+ try {
442
+ const input = {
443
+ sourceId: selected.id,
444
+ file,
445
+ ...isTall ? {} : { uploadYear: year, uploadMonth: month, actualsLagMonths: lag }
446
+ };
447
+ const created = await transport.createUpload(input);
448
+ onCreated(created);
449
+ } catch (e) {
450
+ setError(e instanceof Error ? e.message : "Upload failed");
451
+ } finally {
452
+ setSubmitting(false);
453
+ }
454
+ }, [selected, file, isTall, year, month, lag, transport, onCreated]);
455
+ return /* @__PURE__ */ jsx3("div", { className: "cfi-vi-modal-overlay", role: "dialog", "aria-modal": "true", onClick: onClose, children: /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-modal", onClick: (e) => e.stopPropagation(), children: [
456
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-modal-head", children: [
457
+ /* @__PURE__ */ jsx3("h2", { className: "cfi-vi-modal-title", children: "Upload volume file" }),
458
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "cfi-vi-icon-btn", "aria-label": "Close", onClick: onClose, children: "\u2715" })
459
+ ] }),
460
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-modal-body", children: [
461
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-field", children: [
462
+ /* @__PURE__ */ jsx3("label", { className: "cfi-vi-label", htmlFor: "cfi-vf-source", children: "Source" }),
463
+ /* @__PURE__ */ jsxs3(
464
+ "select",
465
+ {
466
+ id: "cfi-vf-source",
467
+ className: "cfi-vi-select",
468
+ value: selectedSourceId,
469
+ onChange: (e) => setSelectedSourceId(e.target.value),
470
+ children: [
471
+ sources.length === 0 && /* @__PURE__ */ jsx3("option", { value: "", children: "No sources defined" }),
472
+ sources.slice().sort((a, b) => a.name.localeCompare(b.name)).map((s) => /* @__PURE__ */ jsxs3("option", { value: s.id, children: [
473
+ s.name,
474
+ " (",
475
+ s.code,
476
+ ") \xB7 ",
477
+ s.layout,
478
+ s.kind === "overlay" ? " \xB7 overlay" : ""
479
+ ] }, s.id))
480
+ ]
481
+ }
482
+ )
483
+ ] }),
484
+ blockers.length > 0 && /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-alert cfi-vi-alert--warn", children: [
485
+ /* @__PURE__ */ jsx3(AlertTriangle2, { size: 16, "aria-hidden": true }),
486
+ /* @__PURE__ */ jsx3("ul", { className: "cfi-vi-blockers", children: blockers.map((b, i) => /* @__PURE__ */ jsx3("li", { children: b }, i)) })
487
+ ] }),
488
+ selected && !isTall && /* @__PURE__ */ jsxs3(Fragment, { children: [
489
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row", children: [
490
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-field", children: [
491
+ /* @__PURE__ */ jsx3("label", { className: "cfi-vi-label", htmlFor: "cfi-vf-year", children: "Year" }),
492
+ /* @__PURE__ */ jsx3("select", { id: "cfi-vf-year", className: "cfi-vi-select", value: year, onChange: (e) => setYear(Number(e.target.value)), children: YEAR_OPTIONS.map((y) => /* @__PURE__ */ jsx3("option", { value: y, children: y }, y)) })
493
+ ] }),
494
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-field", children: [
495
+ /* @__PURE__ */ jsx3("label", { className: "cfi-vi-label", htmlFor: "cfi-vf-month", children: "Month" }),
496
+ /* @__PURE__ */ jsx3("select", { id: "cfi-vf-month", className: "cfi-vi-select", value: month, onChange: (e) => setMonth(Number(e.target.value)), children: MONTH_NAMES2.map((name, i) => /* @__PURE__ */ jsx3("option", { value: i + 1, children: name }, name)) })
497
+ ] }),
498
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-field", children: [
499
+ /* @__PURE__ */ jsx3("label", { className: "cfi-vi-label", htmlFor: "cfi-vf-lag", children: "Actuals lag (months)" }),
500
+ /* @__PURE__ */ jsx3("select", { id: "cfi-vf-lag", className: "cfi-vi-select", value: lag, onChange: (e) => setLag(Number(e.target.value)), children: [0, 1, 2, 3, 4, 5, 6].map((n) => /* @__PURE__ */ jsxs3("option", { value: n, children: [
501
+ n,
502
+ " month",
503
+ n !== 1 ? "s" : ""
504
+ ] }, n)) })
505
+ ] })
506
+ ] }),
507
+ /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-hint", children: [
508
+ "\u2190 Actuals up to ",
509
+ cutoffLabel,
510
+ " \xB7 Predictions \u2192 from ",
511
+ predictionsLabel
512
+ ] })
513
+ ] }),
514
+ selected && isTall && /* @__PURE__ */ jsx3("p", { className: "cfi-vi-hint", children: "Tall-format upload \u2014 each row carries its own Week Ending date; cutoff fields aren\u2019t needed." }),
515
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-field", children: [
516
+ /* @__PURE__ */ jsx3("label", { className: "cfi-vi-label", htmlFor: "cfi-vf-file", children: "File (.xlsx / .xls)" }),
517
+ /* @__PURE__ */ jsx3(
518
+ "input",
519
+ {
520
+ id: "cfi-vf-file",
521
+ type: "file",
522
+ accept: ".xlsx,.xls",
523
+ className: "cfi-vi-file",
524
+ onChange: (e) => setFile(e.target.files?.[0] ?? null)
525
+ }
526
+ )
527
+ ] }),
528
+ error && /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-alert cfi-vi-alert--error", children: [
529
+ /* @__PURE__ */ jsx3(AlertTriangle2, { size: 16, "aria-hidden": true }),
530
+ " ",
531
+ /* @__PURE__ */ jsx3("span", { children: error })
532
+ ] })
533
+ ] }),
534
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-modal-foot", children: [
535
+ /* @__PURE__ */ jsx3(Btn, { variant: "outline", size: "sm", onClick: onClose, children: "Cancel" }),
536
+ /* @__PURE__ */ jsxs3(Btn, { size: "sm", disabled: submitting || blocked || !file, onClick: handleSubmit, children: [
537
+ submitting ? /* @__PURE__ */ jsx3(Spinner, {}) : /* @__PURE__ */ jsx3(FileUp2, { size: 14, "aria-hidden": true }),
538
+ " ",
539
+ submitting ? "Uploading\u2026" : "Upload"
540
+ ] })
541
+ ] })
542
+ ] }) });
543
+ }
544
+ function VolumeFiles({ transport, className }) {
329
545
  const [sources, setSources] = useState2([]);
330
546
  const [activeSourceId, setActiveSourceId] = useState2(null);
331
- const [templates, setTemplates] = useState2([]);
547
+ const [calendarSeeded, setCalendarSeeded] = useState2(null);
548
+ const [uploads, setUploads] = useState2([]);
549
+ const [total, setTotal] = useState2(0);
332
550
  const [loading, setLoading] = useState2(true);
333
551
  const [error, setError] = useState2(null);
334
- const [showCreate, setShowCreate] = useState2(false);
335
- const [showTemplates, setShowTemplates] = useState2(false);
336
- const [form, setForm] = useState2(EMPTY_FORM);
337
- const [saving, setSaving] = useState2(false);
338
- const [activating, setActivating] = useState2(null);
339
- const [addingTemplate, setAddingTemplate] = useState2(null);
340
- const [confirmActivate, setConfirmActivate] = useState2(null);
341
- const load = useCallback2(async () => {
552
+ const [dialogOpen, setDialogOpen] = useState2(false);
553
+ const [filterStatus, setFilterStatus] = useState2("");
554
+ const [filterSourceId, setFilterSourceId] = useState2("");
555
+ const [offset, setOffset] = useState2(0);
556
+ const [expandedId, setExpandedId] = useState2(null);
557
+ const [errorsById, setErrorsById] = useState2({});
558
+ const load = useCallback2(async (opts) => {
559
+ if (!opts?.silent) setLoading(true);
560
+ try {
561
+ const [srcs, activeId, result, calSeeded] = await Promise.all([
562
+ transport.listSources(),
563
+ transport.getActiveSourceId().catch(() => null),
564
+ transport.listUploads({
565
+ limit: PAGE_SIZE,
566
+ offset,
567
+ ...filterStatus ? { status: filterStatus } : {},
568
+ ...filterSourceId ? { sourceId: filterSourceId } : {}
569
+ }),
570
+ transport.isCalendarSeeded ? transport.isCalendarSeeded().catch(() => null) : Promise.resolve(null)
571
+ ]);
572
+ setSources(srcs);
573
+ setActiveSourceId(activeId);
574
+ setUploads(result.items);
575
+ setTotal(result.total);
576
+ setCalendarSeeded(calSeeded);
577
+ setError(null);
578
+ } catch (e) {
579
+ setError(e instanceof Error ? e.message : "Failed to load uploads");
580
+ } finally {
581
+ setLoading(false);
582
+ }
583
+ }, [transport, offset, filterStatus, filterSourceId]);
584
+ useEffect2(() => {
585
+ void load();
586
+ }, [load]);
587
+ useEffect2(() => {
588
+ const activeIds = uploads.filter((u) => ACTIVE_STATUSES.has(u.status)).map((u) => u.id);
589
+ if (activeIds.length === 0) return;
590
+ const timer = setInterval(async () => {
591
+ const updated = await Promise.allSettled(activeIds.map((id) => transport.getUpload(id)));
592
+ setUploads((prev) => prev.map((u) => {
593
+ const idx = activeIds.indexOf(u.id);
594
+ if (idx < 0) return u;
595
+ const res = updated[idx];
596
+ return res.status === "fulfilled" ? res.value : u;
597
+ }));
598
+ }, POLL_INTERVAL_MS);
599
+ return () => clearInterval(timer);
600
+ }, [uploads, transport]);
601
+ const handleCreated = useCallback2((u) => {
602
+ setDialogOpen(false);
603
+ setUploads((prev) => [u, ...prev.filter((x) => x.id !== u.id)]);
604
+ setTotal((n) => n + 1);
605
+ }, []);
606
+ const handleReprocess = useCallback2(async (id) => {
607
+ try {
608
+ await transport.reprocessUpload(id);
609
+ await load({ silent: true });
610
+ } catch (e) {
611
+ setError(e instanceof Error ? e.message : "Reprocess failed");
612
+ }
613
+ }, [transport, load]);
614
+ const handleDownload = useCallback2(async (id) => {
615
+ try {
616
+ const url = await transport.getUploadDownloadUrl(id);
617
+ window.open(url, "_blank", "noopener");
618
+ } catch (e) {
619
+ setError(e instanceof Error ? e.message : "Download failed");
620
+ }
621
+ }, [transport]);
622
+ const toggleExpand = useCallback2(async (u) => {
623
+ if (expandedId === u.id) {
624
+ setExpandedId(null);
625
+ return;
626
+ }
627
+ setExpandedId(u.id);
628
+ if (!errorsById[u.id] && u.rowsFailed > 0) {
629
+ try {
630
+ const res = await transport.getUploadErrors(u.id, { limit: 100 });
631
+ setErrorsById((prev) => ({ ...prev, [u.id]: res.items }));
632
+ } catch {
633
+ }
634
+ }
635
+ }, [expandedId, errorsById, transport]);
636
+ const totalPages = Math.ceil(total / PAGE_SIZE);
637
+ const currentPage = Math.floor(offset / PAGE_SIZE) + 1;
638
+ const dialogSources = useMemo2(() => sources, [sources]);
639
+ return /* @__PURE__ */ jsxs3(Root, { className, children: [
640
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row cfi-vi-row--between cfi-vi-files-head", children: [
641
+ /* @__PURE__ */ jsx3("h2", { className: "cfi-vi-files-title", children: "Volume uploads" }),
642
+ /* @__PURE__ */ jsxs3(Btn, { size: "sm", onClick: () => setDialogOpen(true), children: [
643
+ /* @__PURE__ */ jsx3(FileUp2, { size: 14, "aria-hidden": true }),
644
+ " Upload file"
645
+ ] })
646
+ ] }),
647
+ error && /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-alert cfi-vi-alert--error", children: [
648
+ /* @__PURE__ */ jsx3(AlertTriangle2, { size: 16, "aria-hidden": true }),
649
+ " ",
650
+ /* @__PURE__ */ jsx3("span", { children: error })
651
+ ] }),
652
+ /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row cfi-vi-files-filters", children: [
653
+ /* @__PURE__ */ jsxs3("select", { className: "cfi-vi-select", value: filterSourceId, onChange: (e) => {
654
+ setFilterSourceId(e.target.value);
655
+ setOffset(0);
656
+ }, children: [
657
+ /* @__PURE__ */ jsx3("option", { value: "", children: "All sources" }),
658
+ sources.map((s) => /* @__PURE__ */ jsx3("option", { value: s.id, children: s.code }, s.id))
659
+ ] }),
660
+ /* @__PURE__ */ jsxs3("select", { className: "cfi-vi-select", value: filterStatus, onChange: (e) => {
661
+ setFilterStatus(e.target.value);
662
+ setOffset(0);
663
+ }, children: [
664
+ /* @__PURE__ */ jsx3("option", { value: "", children: "All statuses" }),
665
+ /* @__PURE__ */ jsx3("option", { value: "pending", children: "Pending" }),
666
+ /* @__PURE__ */ jsx3("option", { value: "processing", children: "Processing" }),
667
+ /* @__PURE__ */ jsx3("option", { value: "done", children: "Done" }),
668
+ /* @__PURE__ */ jsx3("option", { value: "done_with_errors", children: "Done with errors" }),
669
+ /* @__PURE__ */ jsx3("option", { value: "failed", children: "Failed" })
670
+ ] }),
671
+ /* @__PURE__ */ jsx3(Btn, { variant: "ghost", size: "sm", onClick: () => void load(), children: "Refresh" })
672
+ ] }),
673
+ loading ? /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-loading", children: [
674
+ /* @__PURE__ */ jsx3(Spinner, {}),
675
+ " Loading\u2026"
676
+ ] }) : uploads.length === 0 ? /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", children: "No uploads yet." }) : /* @__PURE__ */ jsxs3("table", { className: "cfi-vi-table cfi-vi-files-table", children: [
677
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs3("tr", { children: [
678
+ /* @__PURE__ */ jsx3("th", {}),
679
+ /* @__PURE__ */ jsx3("th", { children: "File" }),
680
+ /* @__PURE__ */ jsx3("th", { children: "Period" }),
681
+ /* @__PURE__ */ jsx3("th", { children: "Source" }),
682
+ /* @__PURE__ */ jsx3("th", { children: "Progress" }),
683
+ /* @__PURE__ */ jsx3("th", { children: "Catalogue" }),
684
+ /* @__PURE__ */ jsx3("th", { children: "Status" }),
685
+ /* @__PURE__ */ jsx3("th", { children: "Duration" }),
686
+ /* @__PURE__ */ jsx3("th", { className: "cfi-vi-ta-right", children: "Actions" })
687
+ ] }) }),
688
+ /* @__PURE__ */ jsx3("tbody", { children: uploads.map((u) => {
689
+ const active = ACTIVE_STATUSES.has(u.status);
690
+ const expanded = expandedId === u.id;
691
+ const catalogue = u.rowsCanonicalised + u.rowsQueued + u.rowsBound;
692
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
693
+ /* @__PURE__ */ jsxs3("tr", { children: [
694
+ /* @__PURE__ */ jsx3("td", { children: /* @__PURE__ */ jsx3("button", { type: "button", className: "cfi-vi-icon-btn", "aria-label": "Toggle details", onClick: () => void toggleExpand(u), children: expanded ? /* @__PURE__ */ jsx3(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx3(ChevronRight, { size: 14 }) }) }),
695
+ /* @__PURE__ */ jsx3("td", { className: "cfi-vi-files-file", title: u.fileName, children: u.fileName }),
696
+ /* @__PURE__ */ jsxs3("td", { className: "cfi-vi-muted", children: [
697
+ u.uploadYear,
698
+ " / ",
699
+ MONTH_ABBR[u.uploadMonth - 1]
700
+ ] }),
701
+ /* @__PURE__ */ jsx3("td", { children: /* @__PURE__ */ jsx3("span", { className: "cfi-vi-tag cfi-vi-tag--outline", children: u.source.code }) }),
702
+ /* @__PURE__ */ jsx3("td", { children: active ? /* @__PURE__ */ jsx3("span", { className: "cfi-vi-muted", children: "processing\u2026" }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
703
+ u.rowsProcessed.toLocaleString(),
704
+ " / ",
705
+ u.rowsTotal.toLocaleString(),
706
+ u.rowsFailed > 0 && /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-danger-text", children: [
707
+ " (",
708
+ u.rowsFailed,
709
+ " failed)"
710
+ ] })
711
+ ] }) }),
712
+ /* @__PURE__ */ jsx3("td", { className: "cfi-vi-muted", children: catalogue === 0 ? "\u2014" : `${u.rowsCanonicalised}\u2713 \xB7 ${u.rowsQueued}\u23F3 \xB7 ${u.rowsBound}\u{1F517}` }),
713
+ /* @__PURE__ */ jsx3("td", { children: /* @__PURE__ */ jsxs3("span", { className: `cfi-vi-tag ${statusTagClass(u.status)}`, children: [
714
+ active && /* @__PURE__ */ jsx3(Spinner, { size: 10 }),
715
+ " ",
716
+ STATUS_LABELS2[u.status] ?? u.status.replace(/_/g, " ")
717
+ ] }) }),
718
+ /* @__PURE__ */ jsx3("td", { className: "cfi-vi-muted", children: formatDuration(u.startedAt, u.completedAt) }),
719
+ /* @__PURE__ */ jsx3("td", { className: "cfi-vi-ta-right", children: /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-files-actions", children: [
720
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "cfi-vi-icon-btn", title: "Download original", onClick: () => void handleDownload(u.id), children: /* @__PURE__ */ jsx3(Download, { size: 14 }) }),
721
+ u.status === "failed" && /* @__PURE__ */ jsx3("button", { type: "button", className: "cfi-vi-icon-btn", title: "Reprocess", onClick: () => void handleReprocess(u.id), children: /* @__PURE__ */ jsx3(RotateCcw2, { size: 14 }) })
722
+ ] }) })
723
+ ] }, u.id),
724
+ expanded && /* @__PURE__ */ jsxs3("tr", { className: "cfi-vi-files-detail", children: [
725
+ /* @__PURE__ */ jsx3("td", {}),
726
+ /* @__PURE__ */ jsxs3("td", { colSpan: 8, children: [
727
+ u.errorSummary && /* @__PURE__ */ jsx3("p", { className: "cfi-vi-danger-text", children: u.errorSummary }),
728
+ u.rowsFailed === 0 ? /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", children: "No row errors." }) : errorsById[u.id]?.length ? /* @__PURE__ */ jsxs3("table", { className: "cfi-vi-errors", children: [
729
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs3("tr", { children: [
730
+ /* @__PURE__ */ jsx3("th", { children: "Row" }),
731
+ /* @__PURE__ */ jsx3("th", { children: "Type" }),
732
+ /* @__PURE__ */ jsx3("th", { children: "Message" })
733
+ ] }) }),
734
+ /* @__PURE__ */ jsx3("tbody", { children: errorsById[u.id].map((e, i) => /* @__PURE__ */ jsxs3("tr", { children: [
735
+ /* @__PURE__ */ jsx3("td", { children: e.rowNumber ?? "\u2014" }),
736
+ /* @__PURE__ */ jsx3("td", { children: e.errorType }),
737
+ /* @__PURE__ */ jsx3("td", { children: e.errorDetail })
738
+ ] }, e.id ?? i)) })
739
+ ] }) : /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-muted", children: [
740
+ u.rowsFailed,
741
+ " error(s) \u2014 loading\u2026"
742
+ ] })
743
+ ] })
744
+ ] }, `${u.id}-detail`)
745
+ ] });
746
+ }) })
747
+ ] }),
748
+ totalPages > 1 && /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row cfi-vi-files-pager", children: [
749
+ /* @__PURE__ */ jsx3(Btn, { variant: "outline", size: "sm", disabled: offset === 0, onClick: () => setOffset((o) => Math.max(0, o - PAGE_SIZE)), children: "\u2190 Prev" }),
750
+ /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-muted", children: [
751
+ "Page ",
752
+ currentPage,
753
+ " of ",
754
+ totalPages
755
+ ] }),
756
+ /* @__PURE__ */ jsx3(Btn, { variant: "outline", size: "sm", disabled: offset + PAGE_SIZE >= total, onClick: () => setOffset((o) => o + PAGE_SIZE), children: "Next \u2192" })
757
+ ] }),
758
+ dialogOpen && /* @__PURE__ */ jsx3(
759
+ UploadDialog,
760
+ {
761
+ transport,
762
+ sources: dialogSources,
763
+ activeSourceId,
764
+ calendarSeeded,
765
+ onClose: () => setDialogOpen(false),
766
+ onCreated: handleCreated
767
+ }
768
+ )
769
+ ] });
770
+ }
771
+
772
+ // src/VolumeSourcesList.tsx
773
+ import { useCallback as useCallback3, useEffect as useEffect3, useState as useState3 } from "react";
774
+ import { CheckCircle2, Sparkles } from "lucide-react";
775
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
776
+ var EMPTY_FORM = { code: "", name: "", description: "", headerRowIndex: 0, volumeHeaderFormat: "MMM yyyy" };
777
+ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras, className }) {
778
+ const [sources, setSources] = useState3([]);
779
+ const [activeSourceId, setActiveSourceId] = useState3(null);
780
+ const [templates, setTemplates] = useState3([]);
781
+ const [loading, setLoading] = useState3(true);
782
+ const [error, setError] = useState3(null);
783
+ const [showCreate, setShowCreate] = useState3(false);
784
+ const [showTemplates, setShowTemplates] = useState3(false);
785
+ const [form, setForm] = useState3(EMPTY_FORM);
786
+ const [saving, setSaving] = useState3(false);
787
+ const [activating, setActivating] = useState3(null);
788
+ const [addingTemplate, setAddingTemplate] = useState3(null);
789
+ const [confirmActivate, setConfirmActivate] = useState3(null);
790
+ const load = useCallback3(async () => {
342
791
  try {
343
792
  setLoading(true);
344
793
  const [allSources, config, allTemplates] = await Promise.all([
@@ -355,11 +804,11 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
355
804
  setLoading(false);
356
805
  }
357
806
  }, [transport]);
358
- useEffect2(() => {
807
+ useEffect3(() => {
359
808
  void load();
360
809
  }, [load]);
361
810
  const availableTemplates = templates.filter((t) => !sources.some((s) => s.code === t.code));
362
- const handleAddFromTemplate = useCallback2(async (code) => {
811
+ const handleAddFromTemplate = useCallback3(async (code) => {
363
812
  setAddingTemplate(code);
364
813
  setError(null);
365
814
  try {
@@ -372,7 +821,7 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
372
821
  setAddingTemplate(null);
373
822
  }
374
823
  }, [transport, onOpenSource]);
375
- const handleCreate = useCallback2(async () => {
824
+ const handleCreate = useCallback3(async () => {
376
825
  if (!form.code || !form.name) return;
377
826
  setSaving(true);
378
827
  try {
@@ -386,7 +835,7 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
386
835
  setSaving(false);
387
836
  }
388
837
  }, [form, transport, load]);
389
- const doActivate = useCallback2(async (sourceId) => {
838
+ const doActivate = useCallback3(async (sourceId) => {
390
839
  setActivating(sourceId);
391
840
  setConfirmActivate(null);
392
841
  try {
@@ -398,7 +847,7 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
398
847
  setActivating(null);
399
848
  }
400
849
  }, [transport, load]);
401
- const handleActivate = useCallback2((src) => {
850
+ const handleActivate = useCallback3((src) => {
402
851
  if (activeSourceId && activeSourceId !== src.id) {
403
852
  setConfirmActivate(src);
404
853
  return;
@@ -406,127 +855,127 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
406
855
  void doActivate(src.id);
407
856
  }, [activeSourceId, doActivate]);
408
857
  const currentActive = sources.find((s) => s.id === activeSourceId);
409
- if (loading) return /* @__PURE__ */ jsx3(Root, { className, children: /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", children: "Loading\u2026" }) });
410
- return /* @__PURE__ */ jsxs3(Root, { className, children: [
411
- /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-list-head", children: [
412
- /* @__PURE__ */ jsxs3("div", { children: [
413
- /* @__PURE__ */ jsx3("h1", { className: "cfi-vi-h1", children: "Volume Sources" }),
414
- currentActive && /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-active-line", children: [
415
- /* @__PURE__ */ jsx3(CheckCircle2, { size: 14, color: "#16a34a", "aria-hidden": true }),
858
+ if (loading) return /* @__PURE__ */ jsx4(Root, { className, children: /* @__PURE__ */ jsx4("p", { className: "cfi-vi-muted", children: "Loading\u2026" }) });
859
+ return /* @__PURE__ */ jsxs4(Root, { className, children: [
860
+ /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-list-head", children: [
861
+ /* @__PURE__ */ jsxs4("div", { children: [
862
+ /* @__PURE__ */ jsx4("h1", { className: "cfi-vi-h1", children: "Volume Sources" }),
863
+ currentActive && /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-active-line", children: [
864
+ /* @__PURE__ */ jsx4(CheckCircle2, { size: 14, color: "#16a34a", "aria-hidden": true }),
416
865
  "Active: ",
417
- /* @__PURE__ */ jsx3("span", { className: "cfi-vi-active-name", children: currentActive.name }),
418
- /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-muted", children: [
866
+ /* @__PURE__ */ jsx4("span", { className: "cfi-vi-active-name", children: currentActive.name }),
867
+ /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-muted", children: [
419
868
  "(",
420
869
  currentActive.code,
421
870
  ")"
422
871
  ] })
423
872
  ] })
424
873
  ] }),
425
- /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row", children: [
426
- availableTemplates.length > 0 && /* @__PURE__ */ jsxs3(Btn, { variant: "outline", size: "sm", onClick: () => setShowTemplates(true), children: [
427
- /* @__PURE__ */ jsx3(Sparkles, { size: 14, "aria-hidden": true }),
874
+ /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-row", children: [
875
+ availableTemplates.length > 0 && /* @__PURE__ */ jsxs4(Btn, { variant: "outline", size: "sm", onClick: () => setShowTemplates(true), children: [
876
+ /* @__PURE__ */ jsx4(Sparkles, { size: 14, "aria-hidden": true }),
428
877
  " Add from template"
429
878
  ] }),
430
- /* @__PURE__ */ jsx3(Btn, { size: "sm", onClick: () => setShowCreate(true), children: "Add Source" })
879
+ /* @__PURE__ */ jsx4(Btn, { size: "sm", onClick: () => setShowCreate(true), children: "Add Source" })
431
880
  ] })
432
881
  ] }),
433
- error && /* @__PURE__ */ jsx3("p", { className: "cfi-vi-error", children: error }),
434
- /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-list", children: [
435
- sources.length === 0 && /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", children: "No sources defined yet." }),
882
+ error && /* @__PURE__ */ jsx4("p", { className: "cfi-vi-error", children: error }),
883
+ /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-list", children: [
884
+ sources.length === 0 && /* @__PURE__ */ jsx4("p", { className: "cfi-vi-muted", children: "No sources defined yet." }),
436
885
  sources.map((src) => {
437
886
  const isActive = src.id === activeSourceId;
438
- return /* @__PURE__ */ jsxs3(Card, { className: isActive ? "cfi-vi-card--active" : "cfi-vi-card--hover", children: [
439
- /* @__PURE__ */ jsx3(CardHeader, { children: /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
440
- /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-card-title", children: [
441
- /* @__PURE__ */ jsx3("button", { type: "button", className: "cfi-vi-link", onClick: () => onOpenSource(src.id), children: src.name }),
442
- isActive && /* @__PURE__ */ jsx3(Badge, { variant: "success", children: "Active" }),
443
- src.kind === "overlay" && /* @__PURE__ */ jsx3(Badge, { variant: "purple", title: "Overlay sources are active whenever data is uploaded \u2014 no Set Active step.", children: "Overlay" })
887
+ return /* @__PURE__ */ jsxs4(Card, { className: isActive ? "cfi-vi-card--active" : "cfi-vi-card--hover", children: [
888
+ /* @__PURE__ */ jsx4(CardHeader, { children: /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
889
+ /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-card-title", children: [
890
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "cfi-vi-link", onClick: () => onOpenSource(src.id), children: src.name }),
891
+ isActive && /* @__PURE__ */ jsx4(Badge, { variant: "success", children: "Active" }),
892
+ src.kind === "overlay" && /* @__PURE__ */ jsx4(Badge, { variant: "purple", title: "Overlay sources are active whenever data is uploaded \u2014 no Set Active step.", children: "Overlay" })
444
893
  ] }),
445
- /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-row", children: [
446
- /* @__PURE__ */ jsx3(Badge, { variant: "outline", children: src.code }),
447
- !src.isActive && /* @__PURE__ */ jsx3(Badge, { variant: "secondary", children: "Disabled" }),
448
- !isActive && src.kind === "base" && /* @__PURE__ */ jsx3(Btn, { variant: "outline", size: "sm", disabled: activating === src.id, onClick: () => handleActivate(src), children: activating === src.id ? "Activating\u2026" : "Set Active" })
894
+ /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-row", children: [
895
+ /* @__PURE__ */ jsx4(Badge, { variant: "outline", children: src.code }),
896
+ !src.isActive && /* @__PURE__ */ jsx4(Badge, { variant: "secondary", children: "Disabled" }),
897
+ !isActive && src.kind === "base" && /* @__PURE__ */ jsx4(Btn, { variant: "outline", size: "sm", disabled: activating === src.id, onClick: () => handleActivate(src), children: activating === src.id ? "Activating\u2026" : "Set Active" })
449
898
  ] })
450
899
  ] }) }),
451
- /* @__PURE__ */ jsxs3(CardBody, { children: [
452
- /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
900
+ /* @__PURE__ */ jsxs4(CardBody, { children: [
901
+ /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
453
902
  src.description || "No description",
454
903
  " \xB7 ",
455
- /* @__PURE__ */ jsx3("span", { className: "cfi-vi-mono", children: src.volumeHeaderFormat })
904
+ /* @__PURE__ */ jsx4("span", { className: "cfi-vi-mono", children: src.volumeHeaderFormat })
456
905
  ] }),
457
- src._count && /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)", marginTop: 4 }, children: [
906
+ src._count && /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)", marginTop: 4 }, children: [
458
907
  src._count.segments,
459
908
  " segments detected"
460
909
  ] }),
461
- isActive && renderActiveSourceExtras && /* @__PURE__ */ jsx3("div", { className: "cfi-vi-active-extras", children: renderActiveSourceExtras(src) })
910
+ isActive && renderActiveSourceExtras && /* @__PURE__ */ jsx4("div", { className: "cfi-vi-active-extras", children: renderActiveSourceExtras(src) })
462
911
  ] })
463
912
  ] }, src.id);
464
913
  })
465
914
  ] }),
466
- /* @__PURE__ */ jsxs3(
915
+ /* @__PURE__ */ jsxs4(
467
916
  Modal,
468
917
  {
469
918
  open: !!confirmActivate,
470
919
  onClose: () => setConfirmActivate(null),
471
920
  title: "Switch active source?",
472
- footer: /* @__PURE__ */ jsxs3(Fragment, { children: [
473
- /* @__PURE__ */ jsx3(Btn, { variant: "outline", onClick: () => setConfirmActivate(null), children: "Cancel" }),
474
- /* @__PURE__ */ jsx3(Btn, { variant: "destructive", onClick: () => confirmActivate && void doActivate(confirmActivate.id), children: "Switch and wipe data" })
921
+ footer: /* @__PURE__ */ jsxs4(Fragment2, { children: [
922
+ /* @__PURE__ */ jsx4(Btn, { variant: "outline", onClick: () => setConfirmActivate(null), children: "Cancel" }),
923
+ /* @__PURE__ */ jsx4(Btn, { variant: "destructive", onClick: () => confirmActivate && void doActivate(confirmActivate.id), children: "Switch and wipe data" })
475
924
  ] }),
476
925
  children: [
477
- /* @__PURE__ */ jsxs3("p", { children: [
926
+ /* @__PURE__ */ jsxs4("p", { children: [
478
927
  "Switching the active source to ",
479
- /* @__PURE__ */ jsx3("strong", { children: confirmActivate?.name }),
928
+ /* @__PURE__ */ jsx4("strong", { children: confirmActivate?.name }),
480
929
  " (",
481
930
  confirmActivate?.code,
482
931
  ")."
483
932
  ] }),
484
- /* @__PURE__ */ jsx3("p", { className: "cfi-vi-error", children: "This permanently wipes all ingested data for this tenant \u2014 volume hierarchy, forecast data, edits, scenarios, uploads, and audit log. Your level 1\u20133 mapping and segment configuration are preserved. This cannot be undone." })
933
+ /* @__PURE__ */ jsx4("p", { className: "cfi-vi-error", children: "This permanently wipes all ingested data for this tenant \u2014 volume hierarchy, forecast data, edits, scenarios, uploads, and audit log. Your level 1\u20133 mapping and segment configuration are preserved. This cannot be undone." })
485
934
  ]
486
935
  }
487
936
  ),
488
- /* @__PURE__ */ jsxs3(
937
+ /* @__PURE__ */ jsxs4(
489
938
  Modal,
490
939
  {
491
940
  open: showCreate,
492
941
  onClose: () => setShowCreate(false),
493
942
  title: "Add volume source",
494
- footer: /* @__PURE__ */ jsxs3(Fragment, { children: [
495
- /* @__PURE__ */ jsx3(Btn, { variant: "outline", onClick: () => setShowCreate(false), children: "Cancel" }),
496
- /* @__PURE__ */ jsx3(Btn, { onClick: handleCreate, disabled: saving || !form.code || !form.name, children: saving ? "Creating\u2026" : "Create" })
943
+ footer: /* @__PURE__ */ jsxs4(Fragment2, { children: [
944
+ /* @__PURE__ */ jsx4(Btn, { variant: "outline", onClick: () => setShowCreate(false), children: "Cancel" }),
945
+ /* @__PURE__ */ jsx4(Btn, { onClick: handleCreate, disabled: saving || !form.code || !form.name, children: saving ? "Creating\u2026" : "Create" })
497
946
  ] }),
498
947
  children: [
499
- /* @__PURE__ */ jsx3(Field, { label: "Code", children: /* @__PURE__ */ jsx3(TextInput, { placeholder: "e.g. IHS", value: form.code, onChange: (e) => setForm((f) => ({ ...f, code: e.target.value.toUpperCase() })) }) }),
500
- /* @__PURE__ */ jsx3(Field, { label: "Name", children: /* @__PURE__ */ jsx3(TextInput, { placeholder: "e.g. IHS Market Production Forecast", value: form.name, onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })) }) }),
501
- /* @__PURE__ */ jsx3(Field, { label: "Description", children: /* @__PURE__ */ jsx3(TextInput, { value: form.description, onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })) }) }),
502
- /* @__PURE__ */ jsx3(Field, { label: "Volume header format", hint: 'e.g. "MMM yyyy" for "Jan 2025", or "MMM_yyyy VehicleVolume" for AFS', children: /* @__PURE__ */ jsx3(TextInput, { placeholder: "MMM yyyy", value: form.volumeHeaderFormat, onChange: (e) => setForm((f) => ({ ...f, volumeHeaderFormat: e.target.value })) }) }),
503
- /* @__PURE__ */ jsx3(Field, { label: "Header row index (0-based)", children: /* @__PURE__ */ jsx3(TextInput, { type: "number", value: form.headerRowIndex, onChange: (e) => setForm((f) => ({ ...f, headerRowIndex: parseInt(e.target.value, 10) || 0 })) }) })
948
+ /* @__PURE__ */ jsx4(Field, { label: "Code", children: /* @__PURE__ */ jsx4(TextInput, { placeholder: "e.g. IHS", value: form.code, onChange: (e) => setForm((f) => ({ ...f, code: e.target.value.toUpperCase() })) }) }),
949
+ /* @__PURE__ */ jsx4(Field, { label: "Name", children: /* @__PURE__ */ jsx4(TextInput, { placeholder: "e.g. IHS Market Production Forecast", value: form.name, onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })) }) }),
950
+ /* @__PURE__ */ jsx4(Field, { label: "Description", children: /* @__PURE__ */ jsx4(TextInput, { value: form.description, onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })) }) }),
951
+ /* @__PURE__ */ jsx4(Field, { label: "Volume header format", hint: 'e.g. "MMM yyyy" for "Jan 2025", or "MMM_yyyy VehicleVolume" for AFS', children: /* @__PURE__ */ jsx4(TextInput, { placeholder: "MMM yyyy", value: form.volumeHeaderFormat, onChange: (e) => setForm((f) => ({ ...f, volumeHeaderFormat: e.target.value })) }) }),
952
+ /* @__PURE__ */ jsx4(Field, { label: "Header row index (0-based)", children: /* @__PURE__ */ jsx4(TextInput, { type: "number", value: form.headerRowIndex, onChange: (e) => setForm((f) => ({ ...f, headerRowIndex: parseInt(e.target.value, 10) || 0 })) }) })
504
953
  ]
505
954
  }
506
955
  ),
507
- /* @__PURE__ */ jsxs3(
956
+ /* @__PURE__ */ jsxs4(
508
957
  Modal,
509
958
  {
510
959
  open: showTemplates,
511
960
  onClose: () => setShowTemplates(false),
512
961
  title: "Add source from template",
513
- footer: /* @__PURE__ */ jsx3(Btn, { variant: "outline", onClick: () => setShowTemplates(false), children: "Close" }),
962
+ footer: /* @__PURE__ */ jsx4(Btn, { variant: "outline", onClick: () => setShowTemplates(false), children: "Close" }),
514
963
  children: [
515
- /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", children: "Templates ship with their segments and default mappings wired up \u2014 pick one to skip manual setup. You can rename, edit columns, or change the layout on the source detail page afterward." }),
516
- availableTemplates.length === 0 ? /* @__PURE__ */ jsxs3("p", { className: "cfi-vi-muted", children: [
964
+ /* @__PURE__ */ jsx4("p", { className: "cfi-vi-muted", children: "Templates ship with their segments and default mappings wired up \u2014 pick one to skip manual setup. You can rename, edit columns, or change the layout on the source detail page afterward." }),
965
+ availableTemplates.length === 0 ? /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-muted", children: [
517
966
  "You already have every available template. Use ",
518
- /* @__PURE__ */ jsx3("strong", { children: "Add Source" }),
967
+ /* @__PURE__ */ jsx4("strong", { children: "Add Source" }),
519
968
  " to add another by hand."
520
- ] }) : availableTemplates.map((t) => /* @__PURE__ */ jsxs3(Card, { className: "cfi-vi-card--hover", children: [
521
- /* @__PURE__ */ jsx3(CardHeader, { children: /* @__PURE__ */ jsxs3("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
522
- /* @__PURE__ */ jsxs3("span", { className: "cfi-vi-card-title", children: [
969
+ ] }) : availableTemplates.map((t) => /* @__PURE__ */ jsxs4(Card, { className: "cfi-vi-card--hover", children: [
970
+ /* @__PURE__ */ jsx4(CardHeader, { children: /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
971
+ /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-card-title", children: [
523
972
  t.name,
524
973
  " ",
525
- /* @__PURE__ */ jsx3(Badge, { variant: "outline", children: t.code })
974
+ /* @__PURE__ */ jsx4(Badge, { variant: "outline", children: t.code })
526
975
  ] }),
527
- /* @__PURE__ */ jsx3(Btn, { size: "sm", onClick: () => void handleAddFromTemplate(t.code), disabled: addingTemplate !== null, children: addingTemplate === t.code ? "Adding\u2026" : "Add" })
976
+ /* @__PURE__ */ jsx4(Btn, { size: "sm", onClick: () => void handleAddFromTemplate(t.code), disabled: addingTemplate !== null, children: addingTemplate === t.code ? "Adding\u2026" : "Add" })
528
977
  ] }) }),
529
- t.description && /* @__PURE__ */ jsx3(CardBody, { children: /* @__PURE__ */ jsx3("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)" }, children: t.description }) })
978
+ t.description && /* @__PURE__ */ jsx4(CardBody, { children: /* @__PURE__ */ jsx4("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)" }, children: t.description }) })
530
979
  ] }, t.code))
531
980
  ]
532
981
  }
@@ -535,7 +984,7 @@ function VolumeSourcesList({ transport, onOpenSource, renderActiveSourceExtras,
535
984
  }
536
985
 
537
986
  // src/UnifiedSegmentMapper.tsx
538
- import { useEffect as useEffect3, useMemo as useMemo2, useState as useState3 } from "react";
987
+ import { useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
539
988
  import { Search, CheckCircle2 as CheckCircle22 } from "lucide-react";
540
989
 
541
990
  // src/constants.ts
@@ -566,7 +1015,7 @@ var AUTOMOTIVE_TRANSFORM_LABELS = {
566
1015
  };
567
1016
 
568
1017
  // src/UnifiedSegmentMapper.tsx
569
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1018
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
570
1019
  var LEVELS = [1, 2, 3, 4, 5, 6];
571
1020
  function UnifiedSegmentMapper({
572
1021
  segments,
@@ -581,12 +1030,12 @@ function UnifiedSegmentMapper({
581
1030
  baseHierarchyHint
582
1031
  }) {
583
1032
  const isSparse = hierarchyMode === "sparse";
584
- const [hierAssign, setHierAssign] = useState3({});
585
- const [autoAssign, setAutoAssign] = useState3({});
586
- const [searchQuery, setSearchQuery] = useState3("");
587
- const [dragging, setDragging] = useState3(null);
588
- const [dropTarget, setDropTarget] = useState3(null);
589
- useEffect3(() => {
1033
+ const [hierAssign, setHierAssign] = useState4({});
1034
+ const [autoAssign, setAutoAssign] = useState4({});
1035
+ const [searchQuery, setSearchQuery] = useState4("");
1036
+ const [dragging, setDragging] = useState4(null);
1037
+ const [dropTarget, setDropTarget] = useState4(null);
1038
+ useEffect4(() => {
590
1039
  const initHier = {};
591
1040
  for (const lv of LEVELS) {
592
1041
  initHier[lv] = hierarchyInitial.find((m) => m.targetKey === `level${lv}`)?.segmentId ?? null;
@@ -602,18 +1051,18 @@ function UnifiedSegmentMapper({
602
1051
  }
603
1052
  setAutoAssign(initAuto);
604
1053
  }, [hierarchyInitial, automotiveInitial, automotiveSlots]);
605
- const segmentById = useMemo2(() => new Map(segments.map((s) => [s.id, s])), [segments]);
606
- const usedIds = useMemo2(() => {
1054
+ const segmentById = useMemo3(() => new Map(segments.map((s) => [s.id, s])), [segments]);
1055
+ const usedIds = useMemo3(() => {
607
1056
  const s = /* @__PURE__ */ new Set();
608
1057
  for (const v of Object.values(hierAssign)) if (v) s.add(v);
609
1058
  for (const a of Object.values(autoAssign)) if (a.segmentId) s.add(a.segmentId);
610
1059
  return s;
611
1060
  }, [hierAssign, autoAssign]);
612
- const poolSegments = useMemo2(() => {
1061
+ const poolSegments = useMemo3(() => {
613
1062
  const q = searchQuery.trim().toLowerCase();
614
1063
  return segments.filter((s) => !usedIds.has(s.id)).filter((s) => !q || s.label.toLowerCase().includes(q)).sort((a, b) => a.label.localeCompare(b.label));
615
1064
  }, [segments, usedIds, searchQuery]);
616
- const hierarchyCoveredKeys = useMemo2(() => {
1065
+ const hierarchyCoveredKeys = useMemo3(() => {
617
1066
  const covered = /* @__PURE__ */ new Set();
618
1067
  for (const slot of automotiveSlots) {
619
1068
  if (slot.hierarchyCoveredByLevel == null) continue;
@@ -622,7 +1071,7 @@ function UnifiedSegmentMapper({
622
1071
  }
623
1072
  return covered;
624
1073
  }, [automotiveSlots, hierAssign, autoAssign]);
625
- const hierarchyErrors = useMemo2(() => {
1074
+ const hierarchyErrors = useMemo3(() => {
626
1075
  if (isSparse) return [];
627
1076
  const errors = [];
628
1077
  for (let n = 2; n <= 6; n++) {
@@ -632,7 +1081,7 @@ function UnifiedSegmentMapper({
632
1081
  }
633
1082
  return errors;
634
1083
  }, [hierAssign, isSparse]);
635
- const missingAutomotive = useMemo2(() => {
1084
+ const missingAutomotive = useMemo3(() => {
636
1085
  const out = [];
637
1086
  for (const slot of automotiveSlots) {
638
1087
  if (hierarchyCoveredKeys.has(slot.key)) continue;
@@ -641,7 +1090,7 @@ function UnifiedSegmentMapper({
641
1090
  }
642
1091
  return out;
643
1092
  }, [automotiveSlots, hierarchyCoveredKeys, hierAssign, autoAssign]);
644
- const isDirty = useMemo2(() => {
1093
+ const isDirty = useMemo3(() => {
645
1094
  for (const lv of LEVELS) {
646
1095
  const cur = hierAssign[lv] ?? null;
647
1096
  const saved = hierarchyInitial.find((m) => m.targetKey === `level${lv}`)?.segmentId ?? null;
@@ -740,14 +1189,14 @@ function UnifiedSegmentMapper({
740
1189
  await onSave(hierEntries, autoEntries);
741
1190
  }
742
1191
  void transformRules;
743
- return /* @__PURE__ */ jsxs4("div", { className: "cfi-vi cfi-vi-mapper", children: [
744
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-mapper-col", children: [
745
- /* @__PURE__ */ jsxs4("section", { className: "cfi-vi-mapper-section", children: [
746
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-mapper-section-head", children: [
747
- /* @__PURE__ */ jsx4("h3", { className: "cfi-vi-mapper-h3", children: "Hierarchy Tree" }),
748
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-mapper-note", children: isSparse ? "Drag chips onto whichever levels your file actually has. Empty levels are fine." : "Drag a chip onto a level. L(n) needs L(n\u22121) first." })
1192
+ return /* @__PURE__ */ jsxs5("div", { className: "cfi-vi cfi-vi-mapper", children: [
1193
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-mapper-col", children: [
1194
+ /* @__PURE__ */ jsxs5("section", { className: "cfi-vi-mapper-section", children: [
1195
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-mapper-section-head", children: [
1196
+ /* @__PURE__ */ jsx5("h3", { className: "cfi-vi-mapper-h3", children: "Hierarchy Tree" }),
1197
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-mapper-note", children: isSparse ? "Drag chips onto whichever levels your file actually has. Empty levels are fine." : "Drag a chip onto a level. L(n) needs L(n\u22121) first." })
749
1198
  ] }),
750
- /* @__PURE__ */ jsx4("div", { className: "cfi-vi-mapper-box", children: LEVELS.map((level) => /* @__PURE__ */ jsx4(
1199
+ /* @__PURE__ */ jsx5("div", { className: "cfi-vi-mapper-box", children: LEVELS.map((level) => /* @__PURE__ */ jsx5(
751
1200
  HierarchyRow,
752
1201
  {
753
1202
  level,
@@ -765,21 +1214,21 @@ function UnifiedSegmentMapper({
765
1214
  },
766
1215
  level
767
1216
  )) }),
768
- hierarchyErrors.length > 0 && /* @__PURE__ */ jsx4("div", { children: hierarchyErrors.map((err) => /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-mapper-err", children: [
1217
+ hierarchyErrors.length > 0 && /* @__PURE__ */ jsx5("div", { children: hierarchyErrors.map((err) => /* @__PURE__ */ jsxs5("p", { className: "cfi-vi-mapper-err", children: [
769
1218
  "\u26A0 ",
770
1219
  err
771
1220
  ] }, err)) })
772
1221
  ] }),
773
- automotiveSlots.length > 0 && /* @__PURE__ */ jsxs4("section", { className: "cfi-vi-mapper-section", children: [
774
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-mapper-section-head", children: [
775
- /* @__PURE__ */ jsx4("h3", { className: "cfi-vi-mapper-h3", children: "Automotive Fields" }),
776
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-mapper-note", children: "Brand / Nameplate / Plant Name come from the hierarchy unless overridden." })
1222
+ automotiveSlots.length > 0 && /* @__PURE__ */ jsxs5("section", { className: "cfi-vi-mapper-section", children: [
1223
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-mapper-section-head", children: [
1224
+ /* @__PURE__ */ jsx5("h3", { className: "cfi-vi-mapper-h3", children: "Automotive Fields" }),
1225
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-mapper-note", children: "Brand / Nameplate / Plant Name come from the hierarchy unless overridden." })
777
1226
  ] }),
778
- /* @__PURE__ */ jsx4("div", { className: "cfi-vi-mapper-box", children: automotiveSlots.map((slot) => {
1227
+ /* @__PURE__ */ jsx5("div", { className: "cfi-vi-mapper-box", children: automotiveSlots.map((slot) => {
779
1228
  const covered = hierarchyCoveredKeys.has(slot.key);
780
1229
  const hierarchySegId = slot.hierarchyCoveredByLevel != null ? hierAssign[slot.hierarchyCoveredByLevel] : null;
781
1230
  const assignment = autoAssign[slot.key];
782
- return /* @__PURE__ */ jsx4(
1231
+ return /* @__PURE__ */ jsx5(
783
1232
  AutomotiveRow,
784
1233
  {
785
1234
  slot,
@@ -801,32 +1250,32 @@ function UnifiedSegmentMapper({
801
1250
  slot.key
802
1251
  );
803
1252
  }) }),
804
- missingAutomotive.length > 0 && /* @__PURE__ */ jsxs4("p", { className: "cfi-vi-mapper-err", children: [
1253
+ missingAutomotive.length > 0 && /* @__PURE__ */ jsxs5("p", { className: "cfi-vi-mapper-err", children: [
805
1254
  "\u26A0 Required: ",
806
1255
  missingAutomotive.join(", ")
807
1256
  ] })
808
1257
  ] }),
809
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-mapper-save", children: [
810
- /* @__PURE__ */ jsx4(Btn, { size: "sm", onClick: handleSave, disabled: !canSave, children: saving ? "Saving\u2026" : "Save Mapping" }),
811
- isDirty && hierarchyErrors.length === 0 && missingAutomotive.length === 0 && !saving && /* @__PURE__ */ jsx4("span", { className: "cfi-vi-mapper-dirty", children: "Unsaved changes" }),
812
- !isDirty && /* @__PURE__ */ jsx4("span", { className: "cfi-vi-muted", children: "Up to date" })
1258
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-mapper-save", children: [
1259
+ /* @__PURE__ */ jsx5(Btn, { size: "sm", onClick: handleSave, disabled: !canSave, children: saving ? "Saving\u2026" : "Save Mapping" }),
1260
+ isDirty && hierarchyErrors.length === 0 && missingAutomotive.length === 0 && !saving && /* @__PURE__ */ jsx5("span", { className: "cfi-vi-mapper-dirty", children: "Unsaved changes" }),
1261
+ !isDirty && /* @__PURE__ */ jsx5("span", { className: "cfi-vi-muted", children: "Up to date" })
813
1262
  ] })
814
1263
  ] }),
815
- /* @__PURE__ */ jsxs4("aside", { className: "cfi-vi-pool-col", children: [
816
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-mapper-section-head", children: [
817
- /* @__PURE__ */ jsx4("h3", { className: "cfi-vi-mapper-h3", children: "Segment Pool" }),
818
- /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-mapper-note", children: [
1264
+ /* @__PURE__ */ jsxs5("aside", { className: "cfi-vi-pool-col", children: [
1265
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-mapper-section-head", children: [
1266
+ /* @__PURE__ */ jsx5("h3", { className: "cfi-vi-mapper-h3", children: "Segment Pool" }),
1267
+ /* @__PURE__ */ jsxs5("span", { className: "cfi-vi-mapper-note", children: [
819
1268
  poolSegments.length,
820
1269
  " of ",
821
1270
  segments.length,
822
1271
  " unused"
823
1272
  ] })
824
1273
  ] }),
825
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-search", children: [
826
- /* @__PURE__ */ jsx4(Search, { size: 14, className: "cfi-vi-search-icon", "aria-hidden": true }),
827
- /* @__PURE__ */ jsx4(TextInput, { type: "search", placeholder: "Filter segments\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })
1274
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-search", children: [
1275
+ /* @__PURE__ */ jsx5(Search, { size: 14, className: "cfi-vi-search-icon", "aria-hidden": true }),
1276
+ /* @__PURE__ */ jsx5(TextInput, { type: "search", placeholder: "Filter segments\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })
828
1277
  ] }),
829
- /* @__PURE__ */ jsx4(
1278
+ /* @__PURE__ */ jsx5(
830
1279
  "div",
831
1280
  {
832
1281
  className: `cfi-vi-pool ${dropTarget === "pool" ? "cfi-vi-pool--active" : ""}`,
@@ -841,10 +1290,10 @@ function UnifiedSegmentMapper({
841
1290
  e.preventDefault();
842
1291
  if (dragging) dropOnPool(dragging);
843
1292
  },
844
- children: segments.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "cfi-vi-pool-empty", children: "No segments detected. Run \u201CDetect segments\u201D from the source page first." }) : poolSegments.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "cfi-vi-pool-empty", children: searchQuery ? "No segments match the filter." : "All segments assigned \u2014 drop a chip here to return it." }) : /* @__PURE__ */ jsx4("div", { className: "cfi-vi-chips", children: poolSegments.map((seg) => /* @__PURE__ */ jsx4(SegmentChip, { label: seg.label, onDragStart: () => startDrag(`pool:${seg.id}`), onDragEnd: endDrag }, seg.id)) })
1293
+ children: segments.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "cfi-vi-pool-empty", children: "No segments detected. Run \u201CDetect segments\u201D from the source page first." }) : poolSegments.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "cfi-vi-pool-empty", children: searchQuery ? "No segments match the filter." : "All segments assigned \u2014 drop a chip here to return it." }) : /* @__PURE__ */ jsx5("div", { className: "cfi-vi-chips", children: poolSegments.map((seg) => /* @__PURE__ */ jsx5(SegmentChip, { label: seg.label, onDragStart: () => startDrag(`pool:${seg.id}`), onDragEnd: endDrag }, seg.id)) })
845
1294
  }
846
1295
  ),
847
- /* @__PURE__ */ jsx4("p", { className: "cfi-vi-mapper-note", children: "Drag chips onto a hierarchy level or an automotive field. Drop a chip back here to unassign." })
1296
+ /* @__PURE__ */ jsx5("p", { className: "cfi-vi-mapper-note", children: "Drag chips onto a hierarchy level or an automotive field. Drop a chip back here to unassign." })
848
1297
  ] })
849
1298
  ] });
850
1299
  }
@@ -866,12 +1315,12 @@ function HierarchyRow({
866
1315
  const isLeaf = level === 6;
867
1316
  const indent = sparse ? 0 : (level - 1) * 22;
868
1317
  const levelName = DEFAULT_LEVEL_NAMES[level];
869
- return /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-hier-row", style: { paddingLeft: `${indent}px` }, children: [
870
- !sparse && level > 1 && /* @__PURE__ */ jsx4("div", { className: "cfi-vi-hier-elbow" }),
871
- /* @__PURE__ */ jsx4("div", { className: `cfi-vi-hier-dot ${seg ? "cfi-vi-hier-dot--filled" : ""}` }),
872
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-hier-name", children: levelName }),
873
- isLeaf && !sparse && /* @__PURE__ */ jsx4("span", { className: "cfi-vi-leaf", children: "Leaf" }),
874
- /* @__PURE__ */ jsx4(
1318
+ return /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-hier-row", style: { paddingLeft: `${indent}px` }, children: [
1319
+ !sparse && level > 1 && /* @__PURE__ */ jsx5("div", { className: "cfi-vi-hier-elbow" }),
1320
+ /* @__PURE__ */ jsx5("div", { className: `cfi-vi-hier-dot ${seg ? "cfi-vi-hier-dot--filled" : ""}` }),
1321
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-hier-name", children: levelName }),
1322
+ isLeaf && !sparse && /* @__PURE__ */ jsx5("span", { className: "cfi-vi-leaf", children: "Leaf" }),
1323
+ /* @__PURE__ */ jsx5(
875
1324
  "div",
876
1325
  {
877
1326
  className: `cfi-vi-dropzone ${isDropTarget ? "cfi-vi-dropzone--active" : seg ? "cfi-vi-dropzone--filled" : "cfi-vi-dropzone--empty"}`,
@@ -886,10 +1335,10 @@ function HierarchyRow({
886
1335
  e.preventDefault();
887
1336
  onDrop();
888
1337
  },
889
- children: seg ? /* @__PURE__ */ jsx4(AssignedChip, { seg, onDragStart: () => onChipDragStart(seg.id), onDragEnd: onChipDragEnd, onRemove: onChipRemove }) : /* @__PURE__ */ jsx4("span", { className: "cfi-vi-dropzone-ph", children: isDropTarget ? "Drop here" : "Drop a segment\u2026" })
1338
+ children: seg ? /* @__PURE__ */ jsx5(AssignedChip, { seg, onDragStart: () => onChipDragStart(seg.id), onDragEnd: onChipDragEnd, onRemove: onChipRemove }) : /* @__PURE__ */ jsx5("span", { className: "cfi-vi-dropzone-ph", children: isDropTarget ? "Drop here" : "Drop a segment\u2026" })
890
1339
  }
891
1340
  ),
892
- baseHint && /* @__PURE__ */ jsxs4("span", { className: "cfi-vi-base-hint", title: `Base source uses "${baseHint}" on ${levelName}. Match here for the overlay to join.`, children: [
1341
+ baseHint && /* @__PURE__ */ jsxs5("span", { className: "cfi-vi-base-hint", title: `Base source uses "${baseHint}" on ${levelName}. Match here for the overlay to join.`, children: [
893
1342
  "base: ",
894
1343
  baseHint
895
1344
  ] })
@@ -913,20 +1362,20 @@ function AutomotiveRow({
913
1362
  }) {
914
1363
  const assignedSeg = assignment?.segmentId ? segmentById.get(assignment.segmentId) : void 0;
915
1364
  const requiredBadge = (slot.required || hierarchyEmpty) && !covered && !assignedSeg;
916
- return /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-auto-row", children: [
917
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-auto-label", children: [
918
- /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-row", children: [
919
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-auto-label-text", children: slot.label }),
920
- requiredBadge && /* @__PURE__ */ jsx4(Badge, { variant: "outline", children: "required" })
1365
+ return /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-auto-row", children: [
1366
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-auto-label", children: [
1367
+ /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-row", children: [
1368
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-auto-label-text", children: slot.label }),
1369
+ requiredBadge && /* @__PURE__ */ jsx5(Badge, { variant: "outline", children: "required" })
921
1370
  ] }),
922
- slot.hierarchyCoveredByLevel != null && /* @__PURE__ */ jsxs4("p", { className: `cfi-vi-auto-cover ${covered ? "cfi-vi-auto-cover--on" : ""}`, children: [
1371
+ slot.hierarchyCoveredByLevel != null && /* @__PURE__ */ jsxs5("p", { className: `cfi-vi-auto-cover ${covered ? "cfi-vi-auto-cover--on" : ""}`, children: [
923
1372
  covered ? "\u2713 " : "",
924
1373
  "From L",
925
1374
  slot.hierarchyCoveredByLevel,
926
1375
  " hierarchy"
927
1376
  ] })
928
1377
  ] }),
929
- /* @__PURE__ */ jsx4(
1378
+ /* @__PURE__ */ jsx5(
930
1379
  "div",
931
1380
  {
932
1381
  className: `cfi-vi-dropzone ${isDropTarget ? "cfi-vi-dropzone--active" : assignedSeg ? "cfi-vi-dropzone--filled" : covered ? "cfi-vi-dropzone--covered" : "cfi-vi-dropzone--empty"}`,
@@ -941,15 +1390,15 @@ function AutomotiveRow({
941
1390
  e.preventDefault();
942
1391
  onDrop();
943
1392
  },
944
- children: assignedSeg ? /* @__PURE__ */ jsx4(AssignedChip, { seg: assignedSeg, onDragStart: () => onChipDragStart(assignedSeg.id), onDragEnd: onChipDragEnd, onRemove: onChipRemove }) : covered && hierarchySegment ? /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-auto-auto", title: "Auto-supplied from hierarchy. Drop a different chip here to override.", children: [
945
- /* @__PURE__ */ jsx4(CheckCircle22, { size: 14, "aria-hidden": true }),
946
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-auto-auto-label", children: hierarchySegment.label }),
947
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-auto-auto-tag", children: "auto" })
948
- ] }) : /* @__PURE__ */ jsx4("span", { className: "cfi-vi-dropzone-ph", children: isDropTarget ? "Drop here" : "Drop a segment\u2026" })
1393
+ children: assignedSeg ? /* @__PURE__ */ jsx5(AssignedChip, { seg: assignedSeg, onDragStart: () => onChipDragStart(assignedSeg.id), onDragEnd: onChipDragEnd, onRemove: onChipRemove }) : covered && hierarchySegment ? /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-auto-auto", title: "Auto-supplied from hierarchy. Drop a different chip here to override.", children: [
1394
+ /* @__PURE__ */ jsx5(CheckCircle22, { size: 14, "aria-hidden": true }),
1395
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-auto-auto-label", children: hierarchySegment.label }),
1396
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-auto-auto-tag", children: "auto" })
1397
+ ] }) : /* @__PURE__ */ jsx5("span", { className: "cfi-vi-dropzone-ph", children: isDropTarget ? "Drop here" : "Drop a segment\u2026" })
949
1398
  }
950
1399
  ),
951
- slot.defaultTransform && !covered && /* @__PURE__ */ jsxs4("div", { className: "cfi-vi-auto-transform", children: [
952
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-auto-transform-label", children: "Transform:" }),
1400
+ slot.defaultTransform && !covered && /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-auto-transform", children: [
1401
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-auto-transform-label", children: "Transform:" }),
953
1402
  " ",
954
1403
  transformRuleLabels[slot.defaultTransform] ?? slot.defaultTransform
955
1404
  ] })
@@ -961,38 +1410,38 @@ function AssignedChip({
961
1410
  onDragEnd,
962
1411
  onRemove
963
1412
  }) {
964
- return /* @__PURE__ */ jsxs4("div", { draggable: true, onDragStart, onDragEnd, className: "cfi-vi-chip--assigned", children: [
965
- /* @__PURE__ */ jsx4("div", { className: "cfi-vi-chip-dot cfi-vi-chip-dot--on" }),
966
- /* @__PURE__ */ jsx4("span", { className: "cfi-vi-chip-label", children: seg.label }),
967
- /* @__PURE__ */ jsx4("button", { type: "button", onClick: (e) => {
1413
+ return /* @__PURE__ */ jsxs5("div", { draggable: true, onDragStart, onDragEnd, className: "cfi-vi-chip--assigned", children: [
1414
+ /* @__PURE__ */ jsx5("div", { className: "cfi-vi-chip-dot cfi-vi-chip-dot--on" }),
1415
+ /* @__PURE__ */ jsx5("span", { className: "cfi-vi-chip-label", children: seg.label }),
1416
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: (e) => {
968
1417
  e.stopPropagation();
969
1418
  onRemove();
970
1419
  }, className: "cfi-vi-chip-x", title: "Remove \u2014 returns to pool", children: "\xD7" })
971
1420
  ] });
972
1421
  }
973
1422
  function SegmentChip({ label, onDragStart, onDragEnd }) {
974
- return /* @__PURE__ */ jsxs4("div", { draggable: true, onDragStart, onDragEnd, className: "cfi-vi-chip", children: [
975
- /* @__PURE__ */ jsx4("div", { className: "cfi-vi-chip-dot" }),
1423
+ return /* @__PURE__ */ jsxs5("div", { draggable: true, onDragStart, onDragEnd, className: "cfi-vi-chip", children: [
1424
+ /* @__PURE__ */ jsx5("div", { className: "cfi-vi-chip-dot" }),
976
1425
  label
977
1426
  ] });
978
1427
  }
979
1428
 
980
1429
  // src/VolumeSourceDetail.tsx
981
- import { useEffect as useEffect6, useRef as useRef2, useState as useState6 } from "react";
982
- import { KeyRound, Lock, Rows, Columns, AlertTriangle as AlertTriangle2, Layers } from "lucide-react";
1430
+ import { useEffect as useEffect7, useRef as useRef3, useState as useState7 } from "react";
1431
+ import { KeyRound, Lock, Rows, Columns, AlertTriangle as AlertTriangle3, Layers, SlidersHorizontal, Tag } from "lucide-react";
983
1432
 
984
1433
  // src/TallLayoutMappings.tsx
985
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
986
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1434
+ import { useEffect as useEffect5, useMemo as useMemo4, useState as useState5 } from "react";
1435
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
987
1436
  var NONE = "__none__";
988
1437
  function TallLayoutMappings({ transport, source, locked }) {
989
- const [loading, setLoading] = useState4(true);
990
- const [saving, setSaving] = useState4(null);
991
- const [error, setError] = useState4(null);
992
- const [periodConfigured, setPeriodConfigured] = useState4(false);
993
- const [valueConfigured, setValueConfigured] = useState4(false);
994
- const [periodSegmentId, setPeriodSegmentId] = useState4(NONE);
995
- const [valueSegmentId, setValueSegmentId] = useState4(NONE);
1438
+ const [loading, setLoading] = useState5(true);
1439
+ const [saving, setSaving] = useState5(null);
1440
+ const [error, setError] = useState5(null);
1441
+ const [periodConfigured, setPeriodConfigured] = useState5(false);
1442
+ const [valueConfigured, setValueConfigured] = useState5(false);
1443
+ const [periodSegmentId, setPeriodSegmentId] = useState5(NONE);
1444
+ const [valueSegmentId, setValueSegmentId] = useState5(NONE);
996
1445
  async function load() {
997
1446
  try {
998
1447
  setLoading(true);
@@ -1010,10 +1459,10 @@ function TallLayoutMappings({ transport, source, locked }) {
1010
1459
  setLoading(false);
1011
1460
  }
1012
1461
  }
1013
- useEffect4(() => {
1462
+ useEffect5(() => {
1014
1463
  void load();
1015
1464
  }, [source.id]);
1016
- const sortedSegments = useMemo3(
1465
+ const sortedSegments = useMemo4(
1017
1466
  () => source.segments.slice().sort((a, b) => a.label.localeCompare(b.label)),
1018
1467
  [source.segments]
1019
1468
  );
@@ -1031,19 +1480,19 @@ function TallLayoutMappings({ transport, source, locked }) {
1031
1480
  setSaving(null);
1032
1481
  }
1033
1482
  }
1034
- if (loading) return /* @__PURE__ */ jsx5("p", { className: "cfi-vi-muted", children: "Loading tall-layout mappings\u2026" });
1483
+ if (loading) return /* @__PURE__ */ jsx6("p", { className: "cfi-vi-muted", children: "Loading tall-layout mappings\u2026" });
1035
1484
  const ready = periodConfigured && valueConfigured;
1036
- return /* @__PURE__ */ jsxs5("section", { className: "cfi-vi-panel", children: [
1037
- /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
1038
- /* @__PURE__ */ jsx5("h2", { className: "cfi-vi-h2", children: "Tall-Layout Mappings" }),
1039
- /* @__PURE__ */ jsx5("span", { style: { fontSize: "var(--cfi-font-size-xs, 12px)", color: ready ? "#16a34a" : "#b45309" }, children: ready ? "Ready for upload" : "Missing period or value mapping" })
1485
+ return /* @__PURE__ */ jsxs6("section", { className: "cfi-vi-panel", children: [
1486
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
1487
+ /* @__PURE__ */ jsx6("h2", { className: "cfi-vi-h2", children: "Tall-Layout Mappings" }),
1488
+ /* @__PURE__ */ jsx6("span", { style: { fontSize: "var(--cfi-font-size-xs, 12px)", color: ready ? "#16a34a" : "#b45309" }, children: ready ? "Ready for upload" : "Missing period or value mapping" })
1040
1489
  ] }),
1041
- /* @__PURE__ */ jsx5("p", { className: "cfi-vi-muted", children: "Tall sources need to know which file column carries the period date and the volume value." }),
1042
- error && /* @__PURE__ */ jsx5("p", { className: "cfi-vi-error", children: error }),
1043
- /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-field", children: [
1044
- /* @__PURE__ */ jsx5("label", { className: "cfi-vi-label", children: "Period column (Week Ending)" }),
1045
- /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-row", children: [
1046
- /* @__PURE__ */ jsxs5(
1490
+ /* @__PURE__ */ jsx6("p", { className: "cfi-vi-muted", children: "Tall sources need to know which file column carries the period date and the volume value." }),
1491
+ error && /* @__PURE__ */ jsx6("p", { className: "cfi-vi-error", children: error }),
1492
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-field", children: [
1493
+ /* @__PURE__ */ jsx6("label", { className: "cfi-vi-label", children: "Period column (Week Ending)" }),
1494
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row", children: [
1495
+ /* @__PURE__ */ jsxs6(
1047
1496
  "select",
1048
1497
  {
1049
1498
  className: "cfi-vi-input",
@@ -1051,18 +1500,18 @@ function TallLayoutMappings({ transport, source, locked }) {
1051
1500
  disabled: locked || saving !== null,
1052
1501
  onChange: (e) => setPeriodSegmentId(e.target.value),
1053
1502
  children: [
1054
- /* @__PURE__ */ jsx5("option", { value: NONE, children: "\u2014 Not mapped \u2014" }),
1055
- sortedSegments.map((s) => /* @__PURE__ */ jsx5("option", { value: s.id, children: s.label }, s.id))
1503
+ /* @__PURE__ */ jsx6("option", { value: NONE, children: "\u2014 Not mapped \u2014" }),
1504
+ sortedSegments.map((s) => /* @__PURE__ */ jsx6("option", { value: s.id, children: s.label }, s.id))
1056
1505
  ]
1057
1506
  }
1058
1507
  ),
1059
- /* @__PURE__ */ jsx5(Btn, { size: "sm", onClick: () => void saveCategory("period", periodSegmentId, "week_date"), disabled: locked || saving !== null, children: saving === "period" ? "Saving\u2026" : "Save" })
1508
+ /* @__PURE__ */ jsx6(Btn, { size: "sm", onClick: () => void saveCategory("period", periodSegmentId, "week_date"), disabled: locked || saving !== null, children: saving === "period" ? "Saving\u2026" : "Save" })
1060
1509
  ] })
1061
1510
  ] }),
1062
- /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-field", children: [
1063
- /* @__PURE__ */ jsx5("label", { className: "cfi-vi-label", children: "Quantity column (Volume)" }),
1064
- /* @__PURE__ */ jsxs5("div", { className: "cfi-vi-row", children: [
1065
- /* @__PURE__ */ jsxs5(
1511
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-field", children: [
1512
+ /* @__PURE__ */ jsx6("label", { className: "cfi-vi-label", children: "Quantity column (Volume)" }),
1513
+ /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row", children: [
1514
+ /* @__PURE__ */ jsxs6(
1066
1515
  "select",
1067
1516
  {
1068
1517
  className: "cfi-vi-input",
@@ -1070,31 +1519,31 @@ function TallLayoutMappings({ transport, source, locked }) {
1070
1519
  disabled: locked || saving !== null,
1071
1520
  onChange: (e) => setValueSegmentId(e.target.value),
1072
1521
  children: [
1073
- /* @__PURE__ */ jsx5("option", { value: NONE, children: "\u2014 Not mapped \u2014" }),
1074
- sortedSegments.map((s) => /* @__PURE__ */ jsx5("option", { value: s.id, children: s.label }, s.id))
1522
+ /* @__PURE__ */ jsx6("option", { value: NONE, children: "\u2014 Not mapped \u2014" }),
1523
+ sortedSegments.map((s) => /* @__PURE__ */ jsx6("option", { value: s.id, children: s.label }, s.id))
1075
1524
  ]
1076
1525
  }
1077
1526
  ),
1078
- /* @__PURE__ */ jsx5(Btn, { size: "sm", onClick: () => void saveCategory("value", valueSegmentId, "quantity"), disabled: locked || saving !== null, children: saving === "value" ? "Saving\u2026" : "Save" })
1527
+ /* @__PURE__ */ jsx6(Btn, { size: "sm", onClick: () => void saveCategory("value", valueSegmentId, "quantity"), disabled: locked || saving !== null, children: saving === "value" ? "Saving\u2026" : "Save" })
1079
1528
  ] })
1080
1529
  ] })
1081
1530
  ] });
1082
1531
  }
1083
1532
 
1084
1533
  // src/ValueAliasesCard.tsx
1085
- import { useEffect as useEffect5, useMemo as useMemo4, useState as useState5 } from "react";
1534
+ import { useEffect as useEffect6, useMemo as useMemo5, useState as useState6 } from "react";
1086
1535
  import { Plus, Trash2, ArrowRight, Save, RotateCw } from "lucide-react";
1087
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1536
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1088
1537
  function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1089
- const [selectedLevel, setSelectedLevel] = useState5(4);
1090
- const [aliases, setAliases] = useState5([]);
1091
- const [draft, setDraft] = useState5([]);
1092
- const [saving, setSaving] = useState5(false);
1093
- const [error, setError] = useState5(null);
1094
- const [loading, setLoading] = useState5(true);
1095
- const [relinking, setRelinking] = useState5(false);
1096
- const [relinkResult, setRelinkResult] = useState5(null);
1097
- useEffect5(() => {
1538
+ const [selectedLevel, setSelectedLevel] = useState6(4);
1539
+ const [aliases, setAliases] = useState6([]);
1540
+ const [draft, setDraft] = useState6([]);
1541
+ const [saving, setSaving] = useState6(false);
1542
+ const [error, setError] = useState6(null);
1543
+ const [loading, setLoading] = useState6(true);
1544
+ const [relinking, setRelinking] = useState6(false);
1545
+ const [relinkResult, setRelinkResult] = useState6(null);
1546
+ useEffect6(() => {
1098
1547
  let cancelled = false;
1099
1548
  setLoading(true);
1100
1549
  void transport.listValueAliases(sourceId).then((items) => {
@@ -1111,12 +1560,12 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1111
1560
  cancelled = true;
1112
1561
  };
1113
1562
  }, [sourceId, transport]);
1114
- useEffect5(() => {
1563
+ useEffect6(() => {
1115
1564
  setDraft(
1116
1565
  aliases.filter((a) => a.level === selectedLevel).map((a) => ({ id: a.id, rawValue: a.rawValue, canonicalValue: a.canonicalValue }))
1117
1566
  );
1118
1567
  }, [aliases, selectedLevel]);
1119
- const isDirty = useMemo4(() => {
1568
+ const isDirty = useMemo5(() => {
1120
1569
  const saved = aliases.filter((a) => a.level === selectedLevel).map((a) => `${a.rawValue}${a.canonicalValue}`).sort();
1121
1570
  const current = draft.filter((d) => d.rawValue.trim() && d.canonicalValue.trim()).map((d) => `${d.rawValue.trim().toUpperCase()}${d.canonicalValue.trim().toUpperCase()}`).sort();
1122
1571
  if (saved.length !== current.length) return true;
@@ -1152,20 +1601,20 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1152
1601
  setSaving(false);
1153
1602
  }
1154
1603
  }
1155
- if (loading) return /* @__PURE__ */ jsx6("p", { className: "cfi-vi-muted", children: "Loading aliases\u2026" });
1156
- return /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-aliases", children: [
1157
- /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
1158
- /* @__PURE__ */ jsxs6("div", { children: [
1159
- /* @__PURE__ */ jsx6("h2", { className: "cfi-vi-h2", children: "Value Aliases" }),
1160
- /* @__PURE__ */ jsxs6("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)" }, children: [
1604
+ if (loading) return /* @__PURE__ */ jsx7("p", { className: "cfi-vi-muted", children: "Loading aliases\u2026" });
1605
+ return /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-aliases", children: [
1606
+ /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row cfi-vi-row--between", children: [
1607
+ /* @__PURE__ */ jsxs7("div", { children: [
1608
+ /* @__PURE__ */ jsx7("h2", { className: "cfi-vi-h2", children: "Value Aliases" }),
1609
+ /* @__PURE__ */ jsxs7("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 11px)" }, children: [
1161
1610
  "Normalise level values when this source uses different vocabulary than the base/overlay it joins to (e.g. ",
1162
- /* @__PURE__ */ jsx6("span", { className: "cfi-vi-mono", children: '"MICHIGAN" \u2192 "MICHIGAN ASSEMBLY"' }),
1611
+ /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mono", children: '"MICHIGAN" \u2192 "MICHIGAN ASSEMBLY"' }),
1163
1612
  "). Applied at match time \u2014 no re-upload."
1164
1613
  ] })
1165
1614
  ] }),
1166
- /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-field", style: { minWidth: 140 }, children: [
1167
- /* @__PURE__ */ jsx6("label", { className: "cfi-vi-label", children: "Level" }),
1168
- /* @__PURE__ */ jsx6("select", { className: "cfi-vi-input", value: String(selectedLevel), onChange: (e) => setSelectedLevel(parseInt(e.target.value, 10)), children: [1, 2, 3, 4, 5, 6].map((n) => /* @__PURE__ */ jsxs6("option", { value: n, children: [
1615
+ /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { minWidth: 140 }, children: [
1616
+ /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Level" }),
1617
+ /* @__PURE__ */ jsx7("select", { className: "cfi-vi-input", value: String(selectedLevel), onChange: (e) => setSelectedLevel(parseInt(e.target.value, 10)), children: [1, 2, 3, 4, 5, 6].map((n) => /* @__PURE__ */ jsxs7("option", { value: n, children: [
1169
1618
  "L",
1170
1619
  n,
1171
1620
  " \u2014 ",
@@ -1173,17 +1622,17 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1173
1622
  ] }, n)) })
1174
1623
  ] })
1175
1624
  ] }),
1176
- /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-panel", children: [
1177
- draft.length === 0 ? /* @__PURE__ */ jsxs6("p", { className: "cfi-vi-muted", style: { fontStyle: "italic", fontSize: "var(--cfi-font-size-xs, 12px)" }, children: [
1625
+ /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-panel", children: [
1626
+ draft.length === 0 ? /* @__PURE__ */ jsxs7("p", { className: "cfi-vi-muted", style: { fontStyle: "italic", fontSize: "var(--cfi-font-size-xs, 12px)" }, children: [
1178
1627
  "No aliases at L",
1179
1628
  selectedLevel,
1180
1629
  " (",
1181
1630
  DEFAULT_LEVEL_NAMES[selectedLevel],
1182
1631
  "). Click ",
1183
- /* @__PURE__ */ jsx6("strong", { children: "+ Add alias" }),
1632
+ /* @__PURE__ */ jsx7("strong", { children: "+ Add alias" }),
1184
1633
  " to create one."
1185
- ] }) : /* @__PURE__ */ jsx6("div", { className: "cfi-vi-alias-rows", children: draft.map((row, i) => /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row", children: [
1186
- /* @__PURE__ */ jsx6(
1634
+ ] }) : /* @__PURE__ */ jsx7("div", { className: "cfi-vi-alias-rows", children: draft.map((row, i) => /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", children: [
1635
+ /* @__PURE__ */ jsx7(
1187
1636
  "input",
1188
1637
  {
1189
1638
  className: "cfi-vi-input cfi-vi-mono",
@@ -1194,8 +1643,8 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1194
1643
  onChange: (e) => updateRow(i, { rawValue: e.target.value })
1195
1644
  }
1196
1645
  ),
1197
- /* @__PURE__ */ jsx6(ArrowRight, { size: 14, className: "cfi-vi-muted", "aria-hidden": true }),
1198
- /* @__PURE__ */ jsx6(
1646
+ /* @__PURE__ */ jsx7(ArrowRight, { size: 14, className: "cfi-vi-muted", "aria-hidden": true }),
1647
+ /* @__PURE__ */ jsx7(
1199
1648
  "input",
1200
1649
  {
1201
1650
  className: "cfi-vi-input cfi-vi-mono",
@@ -1206,21 +1655,21 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1206
1655
  onChange: (e) => updateRow(i, { canonicalValue: e.target.value })
1207
1656
  }
1208
1657
  ),
1209
- /* @__PURE__ */ jsx6("button", { type: "button", className: "cfi-vi-icon-btn", onClick: () => removeRow(i), disabled: locked, title: "Remove row", children: /* @__PURE__ */ jsx6(Trash2, { size: 14, "aria-hidden": true }) })
1658
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "cfi-vi-icon-btn", onClick: () => removeRow(i), disabled: locked, title: "Remove row", children: /* @__PURE__ */ jsx7(Trash2, { size: 14, "aria-hidden": true }) })
1210
1659
  ] }, i)) }),
1211
- /* @__PURE__ */ jsxs6("div", { className: "cfi-vi-row", style: { flexWrap: "wrap", paddingTop: 4 }, children: [
1212
- /* @__PURE__ */ jsxs6(Btn, { variant: "outline", size: "sm", onClick: addRow, disabled: locked, children: [
1213
- /* @__PURE__ */ jsx6(Plus, { size: 14, "aria-hidden": true }),
1660
+ /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { flexWrap: "wrap", paddingTop: 4 }, children: [
1661
+ /* @__PURE__ */ jsxs7(Btn, { variant: "outline", size: "sm", onClick: addRow, disabled: locked, children: [
1662
+ /* @__PURE__ */ jsx7(Plus, { size: 14, "aria-hidden": true }),
1214
1663
  " Add alias"
1215
1664
  ] }),
1216
- /* @__PURE__ */ jsxs6(Btn, { size: "sm", onClick: () => void handleSave(), disabled: !isDirty || saving || locked, children: [
1217
- /* @__PURE__ */ jsx6(Save, { size: 14, "aria-hidden": true }),
1665
+ /* @__PURE__ */ jsxs7(Btn, { size: "sm", onClick: () => void handleSave(), disabled: !isDirty || saving || locked, children: [
1666
+ /* @__PURE__ */ jsx7(Save, { size: 14, "aria-hidden": true }),
1218
1667
  " ",
1219
1668
  saving ? "Saving\u2026" : "Save"
1220
1669
  ] }),
1221
- isDirty && !saving && /* @__PURE__ */ jsx6("span", { className: "cfi-vi-mapper-dirty", children: "Unsaved changes" }),
1222
- !isDirty && !saving && draft.length > 0 && /* @__PURE__ */ jsx6("span", { className: "cfi-vi-muted", children: "Up to date" }),
1223
- isOverlay && /* @__PURE__ */ jsxs6(
1670
+ isDirty && !saving && /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mapper-dirty", children: "Unsaved changes" }),
1671
+ !isDirty && !saving && draft.length > 0 && /* @__PURE__ */ jsx7("span", { className: "cfi-vi-muted", children: "Up to date" }),
1672
+ isOverlay && /* @__PURE__ */ jsxs7(
1224
1673
  Btn,
1225
1674
  {
1226
1675
  variant: "outline",
@@ -1229,46 +1678,47 @@ function ValueAliasesCard({ transport, sourceId, locked, isOverlay }) {
1229
1678
  disabled: relinking || isDirty,
1230
1679
  title: isDirty ? "Save aliases first, then re-link" : "Re-run overlay \u2192 base resolution against current aliases",
1231
1680
  children: [
1232
- /* @__PURE__ */ jsx6(RotateCw, { size: 14, className: relinking ? "cfi-vi-spin" : "", "aria-hidden": true }),
1681
+ /* @__PURE__ */ jsx7(RotateCw, { size: 14, className: relinking ? "cfi-vi-spin" : "", "aria-hidden": true }),
1233
1682
  " ",
1234
1683
  relinking ? "Re-linking\u2026" : "Re-link overlay"
1235
1684
  ]
1236
1685
  }
1237
1686
  )
1238
1687
  ] }),
1239
- error && /* @__PURE__ */ jsx6("p", { className: "cfi-vi-error", children: error }),
1240
- relinkResult && /* @__PURE__ */ jsx6("p", { style: { fontSize: "var(--cfi-font-size-xs, 12px)", color: "#15803d" }, children: relinkResult })
1688
+ error && /* @__PURE__ */ jsx7("p", { className: "cfi-vi-error", children: error }),
1689
+ relinkResult && /* @__PURE__ */ jsx7("p", { style: { fontSize: "var(--cfi-font-size-xs, 12px)", color: "#15803d" }, children: relinkResult })
1241
1690
  ] })
1242
1691
  ] });
1243
1692
  }
1244
1693
 
1245
1694
  // src/VolumeSourceDetail.tsx
1246
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1695
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1247
1696
  function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, className }) {
1248
1697
  const id = sourceId;
1249
- const [source, setSource] = useState6(null);
1250
- const [allSources, setAllSources] = useState6([]);
1251
- const [loading, setLoading] = useState6(true);
1252
- const [error, setError] = useState6(null);
1253
- const [detecting, setDetecting] = useState6(false);
1254
- const [detectMsg, setDetectMsg] = useState6(null);
1255
- const [savingKey, setSavingKey] = useState6(false);
1256
- const [selectedKeyId, setSelectedKeyId] = useState6("__none__");
1257
- const [hasData, setHasData] = useState6(false);
1258
- const fileRef = useRef2(null);
1259
- const [savingLayout, setSavingLayout] = useState6(false);
1260
- const [editLayout, setEditLayout] = useState6("wide");
1261
- const [editValueDateFormat, setEditValueDateFormat] = useState6("yyyy-MM-dd");
1262
- const [newSegmentLabel, setNewSegmentLabel] = useState6("");
1263
- const [newSegmentDataType, setNewSegmentDataType] = useState6("string");
1264
- const [addingSegment, setAddingSegment] = useState6(false);
1265
- const [clearConfirm, setClearConfirm] = useState6("");
1266
- const [clearing, setClearing] = useState6(false);
1267
- const [clearResult, setClearResult] = useState6(null);
1268
- const [mappingKeys, setMappingKeys] = useState6(null);
1269
- const [mappingRows, setMappingRows] = useState6([]);
1270
- const [savingMapping, setSavingMapping] = useState6(false);
1271
- const [baseHierarchyHint, setBaseHierarchyHint] = useState6({});
1698
+ const [source, setSource] = useState7(null);
1699
+ const [allSources, setAllSources] = useState7([]);
1700
+ const [loading, setLoading] = useState7(true);
1701
+ const [error, setError] = useState7(null);
1702
+ const [tab, setTab] = useState7("structure");
1703
+ const [detecting, setDetecting] = useState7(false);
1704
+ const [detectMsg, setDetectMsg] = useState7(null);
1705
+ const [savingKey, setSavingKey] = useState7(false);
1706
+ const [selectedKeyId, setSelectedKeyId] = useState7("__none__");
1707
+ const [hasData, setHasData] = useState7(false);
1708
+ const fileRef = useRef3(null);
1709
+ const [savingLayout, setSavingLayout] = useState7(false);
1710
+ const [editLayout, setEditLayout] = useState7("wide");
1711
+ const [editValueDateFormat, setEditValueDateFormat] = useState7("yyyy-MM-dd");
1712
+ const [newSegmentLabel, setNewSegmentLabel] = useState7("");
1713
+ const [newSegmentDataType, setNewSegmentDataType] = useState7("string");
1714
+ const [addingSegment, setAddingSegment] = useState7(false);
1715
+ const [clearConfirm, setClearConfirm] = useState7("");
1716
+ const [clearing, setClearing] = useState7(false);
1717
+ const [clearResult, setClearResult] = useState7(null);
1718
+ const [mappingKeys, setMappingKeys] = useState7(null);
1719
+ const [mappingRows, setMappingRows] = useState7([]);
1720
+ const [savingMapping, setSavingMapping] = useState7(false);
1721
+ const [baseHierarchyHint, setBaseHierarchyHint] = useState7({});
1272
1722
  async function load() {
1273
1723
  if (!id) return;
1274
1724
  try {
@@ -1321,7 +1771,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1321
1771
  setLoading(false);
1322
1772
  }
1323
1773
  }
1324
- useEffect6(() => {
1774
+ useEffect7(() => {
1325
1775
  void load();
1326
1776
  }, [id]);
1327
1777
  async function handleDetect(file) {
@@ -1420,15 +1870,21 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1420
1870
  setSavingLayout(false);
1421
1871
  }
1422
1872
  }
1423
- if (loading) return /* @__PURE__ */ jsx7(Root, { className, children: /* @__PURE__ */ jsx7("p", { className: "cfi-vi-muted", children: "Loading\u2026" }) });
1424
- if (!source) return /* @__PURE__ */ jsx7(Root, { className, children: /* @__PURE__ */ jsx7("p", { className: "cfi-vi-error", children: "Source not found" }) });
1873
+ if (loading) return /* @__PURE__ */ jsx8(Root, { className, children: /* @__PURE__ */ jsx8("p", { className: "cfi-vi-muted", children: "Loading\u2026" }) });
1874
+ if (!source) return /* @__PURE__ */ jsx8(Root, { className, children: /* @__PURE__ */ jsx8("p", { className: "cfi-vi-error", children: "Source not found" }) });
1425
1875
  const keySegment = source.segments.find((s) => s.id === source.uniqueKeySegmentId);
1426
1876
  const isOverlay = source.kind === "overlay";
1427
- return /* @__PURE__ */ jsx7(Root, { className, children: /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-detail", children: [
1428
- /* @__PURE__ */ jsxs7("div", { children: [
1429
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { gap: 8 }, children: [
1430
- /* @__PURE__ */ jsx7("span", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Source:" }),
1431
- /* @__PURE__ */ jsx7(
1877
+ const TABS = [
1878
+ { id: "structure", label: "Structure", Icon: SlidersHorizontal },
1879
+ { id: "mapping", label: "Mapping", Icon: Layers },
1880
+ { id: "aliases", label: "Aliases", Icon: Tag },
1881
+ { id: "danger", label: "Danger zone", Icon: AlertTriangle3 }
1882
+ ];
1883
+ return /* @__PURE__ */ jsx8(Root, { className, children: /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-detail", children: [
1884
+ /* @__PURE__ */ jsxs8("div", { children: [
1885
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { gap: 8 }, children: [
1886
+ /* @__PURE__ */ jsx8("span", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Source:" }),
1887
+ /* @__PURE__ */ jsx8(
1432
1888
  "select",
1433
1889
  {
1434
1890
  className: "cfi-vi-input",
@@ -1437,7 +1893,7 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1437
1893
  onChange: (e) => {
1438
1894
  if (e.target.value !== source.id) onOpenSource(e.target.value);
1439
1895
  },
1440
- children: allSources.slice().sort((a, b) => a.name.localeCompare(b.name)).map((s) => /* @__PURE__ */ jsxs7("option", { value: s.id, children: [
1896
+ children: allSources.slice().sort((a, b) => a.name.localeCompare(b.name)).map((s) => /* @__PURE__ */ jsxs8("option", { value: s.id, children: [
1441
1897
  s.name,
1442
1898
  " (",
1443
1899
  s.code,
@@ -1446,246 +1902,266 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1446
1902
  ] }, s.id))
1447
1903
  }
1448
1904
  ),
1449
- onBack && /* @__PURE__ */ jsx7("button", { type: "button", className: "cfi-vi-link cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, onClick: onBack, children: "All sources \u2192" })
1905
+ onBack && /* @__PURE__ */ jsx8("button", { type: "button", className: "cfi-vi-link cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, onClick: onBack, children: "All sources \u2192" })
1450
1906
  ] }),
1451
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { flexWrap: "wrap", marginTop: 4 }, children: [
1452
- /* @__PURE__ */ jsx7("h1", { className: "cfi-vi-h1", children: source.name }),
1453
- /* @__PURE__ */ jsx7(Badge, { variant: "outline", children: source.code }),
1454
- isOverlay && /* @__PURE__ */ jsx7(Badge, { variant: "purple", children: "Overlay" })
1907
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { flexWrap: "wrap", marginTop: 4 }, children: [
1908
+ /* @__PURE__ */ jsx8("h1", { className: "cfi-vi-h1", children: source.name }),
1909
+ /* @__PURE__ */ jsx8(Badge, { variant: "outline", children: source.code }),
1910
+ isOverlay && /* @__PURE__ */ jsx8(Badge, { variant: "purple", children: "Overlay" })
1455
1911
  ] }),
1456
- /* @__PURE__ */ jsxs7("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)", marginTop: 2 }, children: [
1912
+ /* @__PURE__ */ jsxs8("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)", marginTop: 2 }, children: [
1457
1913
  "Header row ",
1458
1914
  source.headerRowIndex,
1459
1915
  " \xB7 Format ",
1460
- /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mono", children: source.volumeHeaderFormat }),
1916
+ /* @__PURE__ */ jsx8("span", { className: "cfi-vi-mono", children: source.volumeHeaderFormat }),
1461
1917
  " \xB7",
1462
1918
  " ",
1463
- source.layout === "tall" ? /* @__PURE__ */ jsxs7("span", { className: "cfi-vi-row", style: { display: "inline-flex", gap: 4 }, children: [
1464
- /* @__PURE__ */ jsx7(Rows, { size: 12, "aria-hidden": true }),
1919
+ source.layout === "tall" ? /* @__PURE__ */ jsxs8("span", { className: "cfi-vi-row", style: { display: "inline-flex", gap: 4 }, children: [
1920
+ /* @__PURE__ */ jsx8(Rows, { size: 12, "aria-hidden": true }),
1465
1921
  " tall"
1466
- ] }) : /* @__PURE__ */ jsxs7("span", { className: "cfi-vi-row", style: { display: "inline-flex", gap: 4 }, children: [
1467
- /* @__PURE__ */ jsx7(Columns, { size: 12, "aria-hidden": true }),
1922
+ ] }) : /* @__PURE__ */ jsxs8("span", { className: "cfi-vi-row", style: { display: "inline-flex", gap: 4 }, children: [
1923
+ /* @__PURE__ */ jsx8(Columns, { size: 12, "aria-hidden": true }),
1468
1924
  " wide"
1469
1925
  ] }),
1470
- source.layout === "tall" && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1926
+ source.layout === "tall" && /* @__PURE__ */ jsxs8(Fragment3, { children: [
1471
1927
  " \xB7 Period ",
1472
- /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mono", children: source.valueDateFormat })
1928
+ /* @__PURE__ */ jsx8("span", { className: "cfi-vi-mono", children: source.valueDateFormat })
1473
1929
  ] }),
1474
- source.description && /* @__PURE__ */ jsxs7(Fragment2, { children: [
1930
+ source.description && /* @__PURE__ */ jsxs8(Fragment3, { children: [
1475
1931
  " \xB7 ",
1476
1932
  source.description
1477
1933
  ] })
1478
1934
  ] })
1479
1935
  ] }),
1480
- hasData && /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-lock", children: [
1481
- /* @__PURE__ */ jsx7(Lock, { size: 14, "aria-hidden": true }),
1936
+ hasData && /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-lock", children: [
1937
+ /* @__PURE__ */ jsx8(Lock, { size: 14, "aria-hidden": true }),
1482
1938
  " This source has loaded data. Configuration is locked \u2014 clear data below before making changes."
1483
1939
  ] }),
1484
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-grid2", children: [
1485
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-panel", children: [
1486
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", children: [
1487
- editLayout === "tall" ? /* @__PURE__ */ jsx7(Rows, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }) : /* @__PURE__ */ jsx7(Columns, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
1488
- /* @__PURE__ */ jsx7("h2", { className: "cfi-vi-h2", children: "Layout" })
1940
+ error && /* @__PURE__ */ jsx8("p", { className: "cfi-vi-error", style: { margin: 0 }, children: error }),
1941
+ /* @__PURE__ */ jsx8("div", { className: "cfi-vi-tabs", role: "tablist", children: TABS.map(({ id: id2, label, Icon }) => /* @__PURE__ */ jsxs8(
1942
+ "button",
1943
+ {
1944
+ type: "button",
1945
+ role: "tab",
1946
+ "aria-selected": tab === id2,
1947
+ className: `cfi-vi-tab${tab === id2 ? " cfi-vi-tab--active" : ""}${id2 === "danger" ? " cfi-vi-tab--danger" : ""}`,
1948
+ onClick: () => setTab(id2),
1949
+ children: [
1950
+ /* @__PURE__ */ jsx8(Icon, { size: 14, "aria-hidden": true }),
1951
+ " ",
1952
+ label
1953
+ ]
1954
+ },
1955
+ id2
1956
+ )) }),
1957
+ tab === "structure" && /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-tabpanel", children: [
1958
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-grid2", children: [
1959
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-panel", children: [
1960
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", children: [
1961
+ editLayout === "tall" ? /* @__PURE__ */ jsx8(Rows, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }) : /* @__PURE__ */ jsx8(Columns, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
1962
+ /* @__PURE__ */ jsx8("h2", { className: "cfi-vi-h2", children: "Layout" })
1963
+ ] }),
1964
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { alignItems: "flex-end", gap: 12 }, children: [
1965
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1966
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Layout" }),
1967
+ /* @__PURE__ */ jsxs8(
1968
+ "select",
1969
+ {
1970
+ className: "cfi-vi-input",
1971
+ value: editLayout,
1972
+ disabled: hasData || savingLayout,
1973
+ onChange: (e) => setEditLayout(e.target.value),
1974
+ children: [
1975
+ /* @__PURE__ */ jsx8("option", { value: "wide", children: "wide" }),
1976
+ /* @__PURE__ */ jsx8("option", { value: "tall", children: "tall" })
1977
+ ]
1978
+ }
1979
+ )
1980
+ ] }),
1981
+ editLayout === "tall" && /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1982
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Period format" }),
1983
+ /* @__PURE__ */ jsx8(
1984
+ TextInput,
1985
+ {
1986
+ className: "cfi-vi-mono",
1987
+ value: editValueDateFormat,
1988
+ placeholder: "yyyy-MM-dd",
1989
+ disabled: hasData || savingLayout,
1990
+ onChange: (e) => setEditValueDateFormat(e.target.value)
1991
+ }
1992
+ )
1993
+ ] })
1994
+ ] }),
1995
+ /* @__PURE__ */ jsx8(Btn, { size: "sm", onClick: () => void saveLayoutAndRouting(), disabled: hasData || savingLayout, children: savingLayout ? "Saving\u2026" : "Save layout" })
1489
1996
  ] }),
1490
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { alignItems: "flex-end", gap: 12 }, children: [
1491
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1492
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Layout" }),
1493
- /* @__PURE__ */ jsxs7(
1494
- "select",
1495
- {
1496
- className: "cfi-vi-input",
1497
- value: editLayout,
1498
- disabled: hasData || savingLayout,
1499
- onChange: (e) => setEditLayout(e.target.value),
1500
- children: [
1501
- /* @__PURE__ */ jsx7("option", { value: "wide", children: "wide" }),
1502
- /* @__PURE__ */ jsx7("option", { value: "tall", children: "tall" })
1503
- ]
1504
- }
1505
- )
1997
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-panel", children: [
1998
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", children: [
1999
+ /* @__PURE__ */ jsx8(KeyRound, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
2000
+ /* @__PURE__ */ jsx8("h2", { className: "cfi-vi-h2", children: "Unique Key Column" }),
2001
+ keySegment && /* @__PURE__ */ jsx8(Badge, { variant: "secondary", children: keySegment.label })
1506
2002
  ] }),
1507
- editLayout === "tall" && /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1508
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Period format" }),
1509
- /* @__PURE__ */ jsx7(
1510
- TextInput,
1511
- {
1512
- className: "cfi-vi-mono",
1513
- value: editValueDateFormat,
1514
- placeholder: "yyyy-MM-dd",
1515
- disabled: hasData || savingLayout,
1516
- onChange: (e) => setEditValueDateFormat(e.target.value)
1517
- }
1518
- )
2003
+ source.segments.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Detect segments first to enable this option." }) : /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { alignItems: "flex-end" }, children: [
2004
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
2005
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Key Segment" }),
2006
+ /* @__PURE__ */ jsxs8(
2007
+ "select",
2008
+ {
2009
+ className: "cfi-vi-input",
2010
+ value: selectedKeyId,
2011
+ disabled: hasData,
2012
+ onChange: (e) => setSelectedKeyId(e.target.value),
2013
+ children: [
2014
+ /* @__PURE__ */ jsx8("option", { value: "__none__", children: "None \u2014 use display name" }),
2015
+ source.segments.slice().sort((a, b) => a.label.localeCompare(b.label)).map((seg) => /* @__PURE__ */ jsx8("option", { value: seg.id, children: seg.label }, seg.id))
2016
+ ]
2017
+ }
2018
+ )
2019
+ ] }),
2020
+ /* @__PURE__ */ jsx8(Btn, { size: "sm", onClick: saveUniqueKey, disabled: savingKey || hasData, children: savingKey ? "Saving\u2026" : "Save" })
1519
2021
  ] })
1520
- ] }),
1521
- /* @__PURE__ */ jsx7(Btn, { size: "sm", onClick: () => void saveLayoutAndRouting(), disabled: hasData || savingLayout, children: savingLayout ? "Saving\u2026" : "Save layout" })
2022
+ ] })
1522
2023
  ] }),
1523
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-panel", children: [
1524
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", children: [
1525
- /* @__PURE__ */ jsx7(KeyRound, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
1526
- /* @__PURE__ */ jsx7("h2", { className: "cfi-vi-h2", children: "Unique Key Column" }),
1527
- keySegment && /* @__PURE__ */ jsx7(Badge, { variant: "secondary", children: keySegment.label })
2024
+ /* @__PURE__ */ jsxs8("div", { children: [
2025
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", children: [
2026
+ /* @__PURE__ */ jsxs8("h2", { className: "cfi-vi-h2", children: [
2027
+ "Segments (",
2028
+ source.segments.length,
2029
+ ")"
2030
+ ] }),
2031
+ /* @__PURE__ */ jsx8(Btn, { variant: "outline", size: "sm", onClick: () => fileRef.current?.click(), disabled: detecting || hasData, children: detecting ? "Detecting\u2026" : "Detect from file" }),
2032
+ /* @__PURE__ */ jsx8(
2033
+ "input",
2034
+ {
2035
+ ref: fileRef,
2036
+ type: "file",
2037
+ accept: ".xlsx,.xls",
2038
+ style: { display: "none" },
2039
+ onChange: (e) => {
2040
+ const f = e.target.files?.[0];
2041
+ if (f) void handleDetect(f);
2042
+ e.target.value = "";
2043
+ }
2044
+ }
2045
+ )
1528
2046
  ] }),
1529
- source.segments.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Detect segments first to enable this option." }) : /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { alignItems: "flex-end" }, children: [
1530
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1531
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Key Segment" }),
1532
- /* @__PURE__ */ jsxs7(
1533
- "select",
2047
+ detectMsg && /* @__PURE__ */ jsx8("p", { style: { fontSize: "var(--cfi-font-size-sm, 13px)", color: "#16a34a" }, children: detectMsg }),
2048
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-panel", style: { maxWidth: "42rem" }, children: [
2049
+ /* @__PURE__ */ jsx8("div", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Add segment manually \u2014 for columns not covered by auto-detect." }),
2050
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { alignItems: "flex-end" }, children: [
2051
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
2052
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Label" }),
2053
+ /* @__PURE__ */ jsx8(
2054
+ TextInput,
2055
+ {
2056
+ className: "cfi-vi-mono",
2057
+ value: newSegmentLabel,
2058
+ placeholder: "e.g. Color",
2059
+ disabled: hasData || addingSegment,
2060
+ onChange: (e) => setNewSegmentLabel(e.target.value),
2061
+ onKeyDown: (e) => {
2062
+ if (e.key === "Enter" && newSegmentLabel.trim()) void handleAddSegment();
2063
+ }
2064
+ }
2065
+ )
2066
+ ] }),
2067
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { width: 128 }, children: [
2068
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Data Type" }),
2069
+ /* @__PURE__ */ jsxs8(
2070
+ "select",
2071
+ {
2072
+ className: "cfi-vi-input",
2073
+ value: newSegmentDataType,
2074
+ disabled: hasData || addingSegment,
2075
+ onChange: (e) => setNewSegmentDataType(e.target.value),
2076
+ children: [
2077
+ /* @__PURE__ */ jsx8("option", { value: "string", children: "string" }),
2078
+ /* @__PURE__ */ jsx8("option", { value: "number", children: "number" }),
2079
+ /* @__PURE__ */ jsx8("option", { value: "date", children: "date" })
2080
+ ]
2081
+ }
2082
+ )
2083
+ ] }),
2084
+ /* @__PURE__ */ jsx8(Btn, { size: "sm", onClick: () => void handleAddSegment(), disabled: hasData || addingSegment || !newSegmentLabel.trim(), children: addingSegment ? "Adding\u2026" : "Add segment" })
2085
+ ] })
2086
+ ] }),
2087
+ source.segments.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "cfi-vi-muted", children: "No segments yet. Upload a sample file to auto-detect." }) : /* @__PURE__ */ jsxs8("table", { className: "cfi-vi-table", children: [
2088
+ /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs8("tr", { children: [
2089
+ /* @__PURE__ */ jsx8("th", { style: { width: 48 }, children: "#" }),
2090
+ /* @__PURE__ */ jsx8("th", { children: "Label" }),
2091
+ /* @__PURE__ */ jsx8("th", { children: "Data Type" }),
2092
+ /* @__PURE__ */ jsx8("th", { style: { width: 80 }, children: "Key" }),
2093
+ /* @__PURE__ */ jsx8("th", { style: { width: 64 } })
2094
+ ] }) }),
2095
+ /* @__PURE__ */ jsx8("tbody", { children: source.segments.map((seg) => /* @__PURE__ */ jsxs8("tr", { children: [
2096
+ /* @__PURE__ */ jsx8("td", { className: "cfi-vi-muted", children: seg.sortOrder }),
2097
+ /* @__PURE__ */ jsx8("td", { className: "cfi-vi-mono", children: seg.label }),
2098
+ /* @__PURE__ */ jsx8("td", { className: "cfi-vi-muted", children: seg.dataType }),
2099
+ /* @__PURE__ */ jsx8("td", { children: seg.id === source.uniqueKeySegmentId && /* @__PURE__ */ jsxs8(Badge, { variant: "secondary", children: [
2100
+ /* @__PURE__ */ jsx8(KeyRound, { size: 12, "aria-hidden": true }),
2101
+ " Key"
2102
+ ] }) }),
2103
+ /* @__PURE__ */ jsx8("td", { children: /* @__PURE__ */ jsx8(
2104
+ "button",
1534
2105
  {
1535
- className: "cfi-vi-input",
1536
- value: selectedKeyId,
2106
+ type: "button",
2107
+ className: "cfi-vi-icon-btn",
2108
+ style: { color: "var(--cfi-color-error, #dc2626)" },
2109
+ onClick: () => void handleDelete(seg.id),
1537
2110
  disabled: hasData,
1538
- onChange: (e) => setSelectedKeyId(e.target.value),
1539
- children: [
1540
- /* @__PURE__ */ jsx7("option", { value: "__none__", children: "None \u2014 use display name" }),
1541
- source.segments.slice().sort((a, b) => a.label.localeCompare(b.label)).map((seg) => /* @__PURE__ */ jsx7("option", { value: seg.id, children: seg.label }, seg.id))
1542
- ]
2111
+ title: "Delete segment",
2112
+ children: "\xD7"
1543
2113
  }
1544
- )
1545
- ] }),
1546
- /* @__PURE__ */ jsx7(Btn, { size: "sm", onClick: saveUniqueKey, disabled: savingKey || hasData, children: savingKey ? "Saving\u2026" : "Save" })
2114
+ ) })
2115
+ ] }, seg.id)) })
1547
2116
  ] })
1548
2117
  ] })
1549
2118
  ] }),
1550
- source.layout === "tall" && /* @__PURE__ */ jsx7(TallLayoutMappings, { transport, source, locked: hasData }),
1551
- /* @__PURE__ */ jsxs7("div", { children: [
1552
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", children: [
1553
- /* @__PURE__ */ jsx7(Layers, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
1554
- /* @__PURE__ */ jsx7("h2", { className: "cfi-vi-h2", children: "Segment Mapping" }),
1555
- isOverlay && /* @__PURE__ */ jsx7("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." })
1556
- ] }),
1557
- source.segments.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "cfi-vi-muted", children: "No segments yet. Add segments below (Detect from file or Add manually) before mapping." }) : /* @__PURE__ */ jsx7(
1558
- UnifiedSegmentMapper,
1559
- {
1560
- segments: source.segments.slice().sort((a, b) => a.label.localeCompare(b.label)),
1561
- hierarchyInitial: mappingRows.filter((m) => m.targetCategory === "hierarchy_level"),
1562
- automotiveInitial: mappingRows.filter((m) => m.targetCategory === "automotive_field"),
1563
- automotiveSlots: isOverlay ? [] : AUTOMOTIVE_SLOTS,
1564
- transformRules: mappingKeys?.transformRules ?? [],
1565
- transformRuleLabels: AUTOMOTIVE_TRANSFORM_LABELS,
1566
- onSave: handleSaveMapping,
1567
- saving: savingMapping,
1568
- hierarchyMode: isOverlay ? "sparse" : "tree",
1569
- baseHierarchyHint: isOverlay ? baseHierarchyHint : void 0
1570
- }
1571
- )
1572
- ] }),
1573
- /* @__PURE__ */ jsx7(ValueAliasesCard, { transport, sourceId: source.id, locked: false, isOverlay }),
1574
- /* @__PURE__ */ jsxs7("div", { children: [
1575
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", children: [
1576
- /* @__PURE__ */ jsxs7("h2", { className: "cfi-vi-h2", children: [
1577
- "Segments (",
1578
- source.segments.length,
1579
- ")"
2119
+ tab === "mapping" && /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-tabpanel", children: [
2120
+ source.layout === "tall" && /* @__PURE__ */ jsx8(TallLayoutMappings, { transport, source, locked: hasData }),
2121
+ /* @__PURE__ */ jsxs8("div", { children: [
2122
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", children: [
2123
+ /* @__PURE__ */ jsx8(Layers, { size: 16, className: "cfi-vi-muted", "aria-hidden": true }),
2124
+ /* @__PURE__ */ jsx8("h2", { className: "cfi-vi-h2", children: "Segment Mapping" }),
2125
+ 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." })
1580
2126
  ] }),
1581
- /* @__PURE__ */ jsx7(Btn, { variant: "outline", size: "sm", onClick: () => fileRef.current?.click(), disabled: detecting || hasData, children: detecting ? "Detecting\u2026" : "Detect from file" }),
1582
- /* @__PURE__ */ jsx7(
1583
- "input",
2127
+ 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(
2128
+ UnifiedSegmentMapper,
1584
2129
  {
1585
- ref: fileRef,
1586
- type: "file",
1587
- accept: ".xlsx,.xls",
1588
- style: { display: "none" },
1589
- onChange: (e) => {
1590
- const f = e.target.files?.[0];
1591
- if (f) void handleDetect(f);
1592
- e.target.value = "";
1593
- }
2130
+ segments: source.segments.slice().sort((a, b) => a.label.localeCompare(b.label)),
2131
+ hierarchyInitial: mappingRows.filter((m) => m.targetCategory === "hierarchy_level"),
2132
+ automotiveInitial: mappingRows.filter((m) => m.targetCategory === "automotive_field"),
2133
+ automotiveSlots: isOverlay ? [] : AUTOMOTIVE_SLOTS,
2134
+ transformRules: mappingKeys?.transformRules ?? [],
2135
+ transformRuleLabels: AUTOMOTIVE_TRANSFORM_LABELS,
2136
+ onSave: handleSaveMapping,
2137
+ saving: savingMapping,
2138
+ hierarchyMode: isOverlay ? "sparse" : "tree",
2139
+ baseHierarchyHint: isOverlay ? baseHierarchyHint : void 0
1594
2140
  }
1595
2141
  )
1596
- ] }),
1597
- detectMsg && /* @__PURE__ */ jsx7("p", { style: { fontSize: "var(--cfi-font-size-sm, 13px)", color: "#16a34a" }, children: detectMsg }),
1598
- error && /* @__PURE__ */ jsx7("p", { className: "cfi-vi-error", children: error }),
1599
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-panel", style: { maxWidth: "42rem" }, children: [
1600
- /* @__PURE__ */ jsx7("div", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-xs, 12px)" }, children: "Add segment manually \u2014 for columns not covered by auto-detect." }),
1601
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { alignItems: "flex-end" }, children: [
1602
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1603
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Label" }),
1604
- /* @__PURE__ */ jsx7(
1605
- TextInput,
1606
- {
1607
- className: "cfi-vi-mono",
1608
- value: newSegmentLabel,
1609
- placeholder: "e.g. Color",
1610
- disabled: hasData || addingSegment,
1611
- onChange: (e) => setNewSegmentLabel(e.target.value),
1612
- onKeyDown: (e) => {
1613
- if (e.key === "Enter" && newSegmentLabel.trim()) void handleAddSegment();
1614
- }
1615
- }
1616
- )
1617
- ] }),
1618
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { width: 128 }, children: [
1619
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Data Type" }),
1620
- /* @__PURE__ */ jsxs7(
1621
- "select",
1622
- {
1623
- className: "cfi-vi-input",
1624
- value: newSegmentDataType,
1625
- disabled: hasData || addingSegment,
1626
- onChange: (e) => setNewSegmentDataType(e.target.value),
1627
- children: [
1628
- /* @__PURE__ */ jsx7("option", { value: "string", children: "string" }),
1629
- /* @__PURE__ */ jsx7("option", { value: "number", children: "number" }),
1630
- /* @__PURE__ */ jsx7("option", { value: "date", children: "date" })
1631
- ]
1632
- }
1633
- )
1634
- ] }),
1635
- /* @__PURE__ */ jsx7(Btn, { size: "sm", onClick: () => void handleAddSegment(), disabled: hasData || addingSegment || !newSegmentLabel.trim(), children: addingSegment ? "Adding\u2026" : "Add segment" })
1636
- ] })
1637
- ] }),
1638
- source.segments.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "cfi-vi-muted", children: "No segments yet. Upload a sample file to auto-detect." }) : /* @__PURE__ */ jsxs7("table", { className: "cfi-vi-table", children: [
1639
- /* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs7("tr", { children: [
1640
- /* @__PURE__ */ jsx7("th", { style: { width: 48 }, children: "#" }),
1641
- /* @__PURE__ */ jsx7("th", { children: "Label" }),
1642
- /* @__PURE__ */ jsx7("th", { children: "Data Type" }),
1643
- /* @__PURE__ */ jsx7("th", { style: { width: 80 }, children: "Key" }),
1644
- /* @__PURE__ */ jsx7("th", { style: { width: 64 } })
1645
- ] }) }),
1646
- /* @__PURE__ */ jsx7("tbody", { children: source.segments.map((seg) => /* @__PURE__ */ jsxs7("tr", { children: [
1647
- /* @__PURE__ */ jsx7("td", { className: "cfi-vi-muted", children: seg.sortOrder }),
1648
- /* @__PURE__ */ jsx7("td", { className: "cfi-vi-mono", children: seg.label }),
1649
- /* @__PURE__ */ jsx7("td", { className: "cfi-vi-muted", children: seg.dataType }),
1650
- /* @__PURE__ */ jsx7("td", { children: seg.id === source.uniqueKeySegmentId && /* @__PURE__ */ jsxs7(Badge, { variant: "secondary", children: [
1651
- /* @__PURE__ */ jsx7(KeyRound, { size: 12, "aria-hidden": true }),
1652
- " Key"
1653
- ] }) }),
1654
- /* @__PURE__ */ jsx7("td", { children: /* @__PURE__ */ jsx7(
1655
- "button",
1656
- {
1657
- type: "button",
1658
- className: "cfi-vi-icon-btn",
1659
- style: { color: "var(--cfi-color-error, #dc2626)" },
1660
- onClick: () => void handleDelete(seg.id),
1661
- disabled: hasData,
1662
- title: "Delete segment",
1663
- children: "\xD7"
1664
- }
1665
- ) })
1666
- ] }, seg.id)) })
1667
2142
  ] })
1668
2143
  ] }),
1669
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-danger", children: [
1670
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { color: "var(--cfi-color-error, #dc2626)" }, children: [
1671
- /* @__PURE__ */ jsx7(AlertTriangle2, { size: 16, "aria-hidden": true }),
1672
- /* @__PURE__ */ jsx7("h2", { className: "cfi-vi-h2", children: "Danger zone \u2014 clear source data" })
2144
+ tab === "aliases" && /* @__PURE__ */ jsx8("div", { className: "cfi-vi-tabpanel", children: /* @__PURE__ */ jsx8(ValueAliasesCard, { transport, sourceId: source.id, locked: false, isOverlay }) }),
2145
+ tab === "danger" && /* @__PURE__ */ jsx8("div", { className: "cfi-vi-tabpanel", children: /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-danger", children: [
2146
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { color: "var(--cfi-color-error, #dc2626)" }, children: [
2147
+ /* @__PURE__ */ jsx8(AlertTriangle3, { size: 16, "aria-hidden": true }),
2148
+ /* @__PURE__ */ jsx8("h2", { className: "cfi-vi-h2", children: "Danger zone \u2014 clear source data" })
1673
2149
  ] }),
1674
- /* @__PURE__ */ jsxs7("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
2150
+ /* @__PURE__ */ jsxs8("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
1675
2151
  "Deletes every upload, criteria, volume actual, prediction, and forecast edit tied to",
1676
2152
  " ",
1677
- /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mono", children: source.name }),
2153
+ /* @__PURE__ */ jsx8("span", { className: "cfi-vi-mono", children: source.name }),
1678
2154
  ". Other sources are untouched. Configuration (segments, mappings, key column, layout) stays in place. There is no undo."
1679
2155
  ] }),
1680
- /* @__PURE__ */ jsxs7("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
2156
+ /* @__PURE__ */ jsxs8("p", { className: "cfi-vi-muted", style: { fontSize: "var(--cfi-font-size-sm, 13px)" }, children: [
1681
2157
  "To confirm, type ",
1682
- /* @__PURE__ */ jsx7("span", { className: "cfi-vi-mono", style: { fontWeight: 600 }, children: "DELETE" }),
2158
+ /* @__PURE__ */ jsx8("span", { className: "cfi-vi-mono", style: { fontWeight: 600 }, children: "DELETE" }),
1683
2159
  " below."
1684
2160
  ] }),
1685
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-row", style: { alignItems: "flex-end", maxWidth: "28rem" }, children: [
1686
- /* @__PURE__ */ jsxs7("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
1687
- /* @__PURE__ */ jsx7("label", { className: "cfi-vi-label", children: "Confirmation" }),
1688
- /* @__PURE__ */ jsx7(
2161
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-row", style: { alignItems: "flex-end", maxWidth: "28rem" }, children: [
2162
+ /* @__PURE__ */ jsxs8("div", { className: "cfi-vi-field", style: { flex: 1 }, children: [
2163
+ /* @__PURE__ */ jsx8("label", { className: "cfi-vi-label", children: "Confirmation" }),
2164
+ /* @__PURE__ */ jsx8(
1689
2165
  TextInput,
1690
2166
  {
1691
2167
  className: "cfi-vi-mono",
@@ -1696,10 +2172,10 @@ function VolumeSourceDetail({ transport, sourceId, onOpenSource, onBack, classNa
1696
2172
  }
1697
2173
  )
1698
2174
  ] }),
1699
- /* @__PURE__ */ jsx7(Btn, { variant: "destructive", size: "sm", onClick: () => void handleClearData(), disabled: clearConfirm !== "DELETE" || clearing, children: clearing ? "Clearing\u2026" : "Clear all data" })
2175
+ /* @__PURE__ */ jsx8(Btn, { variant: "destructive", size: "sm", onClick: () => void handleClearData(), disabled: clearConfirm !== "DELETE" || clearing, children: clearing ? "Clearing\u2026" : "Clear all data" })
1700
2176
  ] }),
1701
- clearResult && /* @__PURE__ */ jsx7("p", { style: { fontSize: "var(--cfi-font-size-sm, 13px)", color: "#15803d" }, children: clearResult })
1702
- ] })
2177
+ clearResult && /* @__PURE__ */ jsx8("p", { style: { fontSize: "var(--cfi-font-size-sm, 13px)", color: "#15803d" }, children: clearResult })
2178
+ ] }) })
1703
2179
  ] }) });
1704
2180
  }
1705
2181
  export {
@@ -1710,6 +2186,7 @@ export {
1710
2186
  TallLayoutMappings,
1711
2187
  UnifiedSegmentMapper,
1712
2188
  ValueAliasesCard,
2189
+ VolumeFiles,
1713
2190
  VolumeImport,
1714
2191
  VolumeSourceDetail,
1715
2192
  VolumeSourcesList