jupiter-dynamic-forms 1.19.7 → 1.19.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1805,6 +1805,15 @@ const error$1 = {
1805
1805
  ok: "OK"
1806
1806
  }
1807
1807
  };
1808
+ const calculationWarning$1 = {
1809
+ title: "Calculation warning",
1810
+ summaryLine: 'Total "{{label}}" should equal {{calculated}}, but you entered {{entered}}.',
1811
+ childrenPrefix: "Children:",
1812
+ childAdded: "{{label}} = {{value}} (added)",
1813
+ childSubtracted: "{{label}} = {{value}} (subtracted, opposite balance)",
1814
+ added: "Added",
1815
+ subtracted: "Subtracted (opposite balance)"
1816
+ };
1808
1817
  const enTranslations = {
1809
1818
  conceptInfo: conceptInfo$1,
1810
1819
  form: form$1,
@@ -1815,7 +1824,8 @@ const enTranslations = {
1815
1824
  admin: admin$1,
1816
1825
  validation: validation$1,
1817
1826
  xbrlValidation: xbrlValidation$1,
1818
- error: error$1
1827
+ error: error$1,
1828
+ calculationWarning: calculationWarning$1
1819
1829
  };
1820
1830
  const conceptInfo = {
1821
1831
  title: "Conceptinformatie",
@@ -1990,6 +2000,15 @@ const error = {
1990
2000
  ok: "OK"
1991
2001
  }
1992
2002
  };
2003
+ const calculationWarning = {
2004
+ title: "Berekeningswaarschuwing",
2005
+ summaryLine: 'Totaal "{{label}}" zou {{calculated}} moeten zijn, maar u heeft {{entered}} ingevoerd.',
2006
+ childrenPrefix: "Onderdelen:",
2007
+ childAdded: "{{label}} = {{value}} (opgeteld)",
2008
+ childSubtracted: "{{label}} = {{value}} (afgetrokken, tegengestelde balans)",
2009
+ added: "Opgeteld",
2010
+ subtracted: "Afgetrokken (tegengestelde balans)"
2011
+ };
1993
2012
  const nlTranslations = {
1994
2013
  conceptInfo,
1995
2014
  form,
@@ -2000,7 +2019,8 @@ const nlTranslations = {
2000
2019
  admin,
2001
2020
  validation,
2002
2021
  xbrlValidation,
2003
- error
2022
+ error,
2023
+ calculationWarning
2004
2024
  };
2005
2025
  const translations = {
2006
2026
  en: enTranslations,
@@ -4936,6 +4956,79 @@ let JupiterConceptTree = class extends LitElement {
4936
4956
  dialog.addEventListener("close", () => document.body.removeChild(dialog));
4937
4957
  dialog.showModal();
4938
4958
  }
4959
+ _ensureCalcWarningDialogStyles() {
4960
+ if (document.getElementById("jdf-calc-warning-dialog-styles"))
4961
+ return;
4962
+ const style = document.createElement("style");
4963
+ style.id = "jdf-calc-warning-dialog-styles";
4964
+ style.textContent = `
4965
+ dialog.jdf-calc-warning-dialog {
4966
+ border: none; border-radius: 8px; padding: 0;
4967
+ max-width: 480px; width: 90vw;
4968
+ box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden;
4969
+ }
4970
+ dialog.jdf-calc-warning-dialog::backdrop { background: rgba(0,0,0,0.35); }
4971
+ .jdf-calc-warning-header {
4972
+ background: #fff3e0; padding: 14px 20px;
4973
+ display: flex; align-items: center; justify-content: space-between;
4974
+ border-bottom: 1px solid #ffcc80;
4975
+ }
4976
+ .jdf-calc-warning-title { font-size: 15px; font-weight: 600; color: #e65100; margin: 0; font-family: inherit; }
4977
+ .jdf-calc-warning-close-btn {
4978
+ width: 28px; height: 28px; border: none; background: transparent;
4979
+ font-size: 20px; line-height: 1; cursor: pointer; color: #666;
4980
+ border-radius: 4px; display: flex; align-items: center; justify-content: center;
4981
+ padding: 0; font-family: inherit;
4982
+ }
4983
+ .jdf-calc-warning-close-btn:hover { background: #ffe0b2; color: #e65100; }
4984
+ .jdf-calc-warning-body { padding: 20px; font-family: inherit; font-size: 13px; color: #333; }
4985
+ .jdf-calc-warning-summary { margin: 0 0 12px 0; font-weight: 600; }
4986
+ .jdf-calc-warning-table { width: 100%; border-collapse: collapse; font-size: 13px; font-family: inherit; }
4987
+ .jdf-calc-warning-table tr:not(:last-child) td { border-bottom: 1px solid #f0f0f0; }
4988
+ .jdf-calc-warning-table td { padding: 6px 4px; vertical-align: top; }
4989
+ .jdf-calc-warning-sign { font-weight: 600; white-space: nowrap; }
4990
+ .jdf-calc-warning-sign.added { color: #2e7d32; }
4991
+ .jdf-calc-warning-sign.subtracted { color: #c62828; }
4992
+ `;
4993
+ document.head.appendChild(style);
4994
+ }
4995
+ _openCalculationWarningDialog(e2, mismatch) {
4996
+ var _a;
4997
+ e2.stopPropagation();
4998
+ this._ensureCalcWarningDialogStyles();
4999
+ const rowsHtml = mismatch.children.map((child) => `
5000
+ <tr>
5001
+ <td>${this._escapeHtml(child.label)}</td>
5002
+ <td>${this._escapeHtml(String(child.value))}</td>
5003
+ <td>${this._escapeHtml(child.balance || "—")}</td>
5004
+ <td class="jdf-calc-warning-sign ${child.weight === 1 ? "added" : "subtracted"}">
5005
+ ${this._escapeHtml(child.weight === 1 ? I18n.t("calculationWarning.added") : I18n.t("calculationWarning.subtracted"))}
5006
+ </td>
5007
+ </tr>
5008
+ `).join("");
5009
+ const dialog = document.createElement("dialog");
5010
+ dialog.className = "jdf-calc-warning-dialog";
5011
+ dialog.innerHTML = `
5012
+ <div class="jdf-calc-warning-header">
5013
+ <h3 class="jdf-calc-warning-title">⚠️ ${this._escapeHtml(I18n.t("calculationWarning.title"))}</h3>
5014
+ <button class="jdf-calc-warning-close-btn" type="button" aria-label="${this._escapeHtml(I18n.t("conceptInfo.close"))}">×</button>
5015
+ </div>
5016
+ <div class="jdf-calc-warning-body">
5017
+ <p class="jdf-calc-warning-summary">${this._escapeHtml(mismatch.message)}</p>
5018
+ <table class="jdf-calc-warning-table">${rowsHtml}</table>
5019
+ </div>
5020
+ `;
5021
+ document.body.appendChild(dialog);
5022
+ (_a = dialog.querySelector(".jdf-calc-warning-close-btn")) == null ? void 0 : _a.addEventListener("click", () => dialog.close());
5023
+ dialog.addEventListener("click", (ev) => {
5024
+ const rect = dialog.getBoundingClientRect();
5025
+ if (ev.clientX < rect.left || ev.clientX > rect.right || ev.clientY < rect.top || ev.clientY > rect.bottom) {
5026
+ dialog.close();
5027
+ }
5028
+ });
5029
+ dialog.addEventListener("close", () => document.body.removeChild(dialog));
5030
+ dialog.showModal();
5031
+ }
4939
5032
  render() {
4940
5033
  const hasChildren = this.concept.children && this.concept.children.length > 0;
4941
5034
  const level = this.concept.level || 0;
@@ -4980,8 +5073,17 @@ let JupiterConceptTree = class extends LitElement {
4980
5073
  const shouldShowField = !isAbstract && field2;
4981
5074
  const storedUnit = (_b = (_a = this.unitData) == null ? void 0 : _a[this.concept.id]) == null ? void 0 : _b[column2.id];
4982
5075
  const storedDecimals = (_d = (_c = this.decimalsData) == null ? void 0 : _c[this.concept.id]) == null ? void 0 : _d[column2.id];
5076
+ const calcMismatch = this.mode !== "readonly" ? (_e = this.calculationMismatches) == null ? void 0 : _e.get(`${this.concept.id}__${column2.id}`) : void 0;
4983
5077
  return html`
4984
- <td class="field-cell ${!shouldShowField ? "empty" : ""} ${isAbstract ? "abstract-row" : ""} ${this.highlightType && column2.id === this.highlightColumnId ? "highlight-" + this.highlightType : ""} ${((_e = this.calculationErrorKeys) == null ? void 0 : _e.has(`${this.concept.id}__${column2.id}`)) ? "calc-error" : ""} ${this.rowFocused ? "row-focused" : ""}">
5078
+ <td class="field-cell ${!shouldShowField ? "empty" : ""} ${isAbstract ? "abstract-row" : ""} ${this.highlightType && column2.id === this.highlightColumnId ? "highlight-" + this.highlightType : ""} ${calcMismatch ? "calc-warning" : ""} ${this.rowFocused ? "row-focused" : ""}">
5079
+ ${calcMismatch ? html`
5080
+ <button
5081
+ class="calc-warning-badge"
5082
+ type="button"
5083
+ title="${calcMismatch.message}"
5084
+ @click="${(e2) => this._openCalculationWarningDialog(e2, calcMismatch)}"
5085
+ >!</button>
5086
+ ` : ""}
4985
5087
  ${shouldShowField ? html`
4986
5088
  <jupiter-form-field
4987
5089
  .field="${field2}"
@@ -5118,6 +5220,7 @@ JupiterConceptTree.styles = css`
5118
5220
  }
5119
5221
 
5120
5222
  .field-cell {
5223
+ position: relative;
5121
5224
  vertical-align: middle;
5122
5225
  padding: 2px 6px;
5123
5226
  border: 1px solid var(--primaryTextColor, var(--jupiter-border-color, #ddd));
@@ -5192,8 +5295,35 @@ JupiterConceptTree.styles = css`
5192
5295
  background: rgba(25, 118, 210, 0.07);
5193
5296
  }
5194
5297
 
5195
- .field-cell.calc-error {
5196
- box-shadow: inset 0 0 0 2px var(--jupiter-error-color, #d32f2f);
5298
+ /* Non-blocking JDF-036 live total/debit-credit mismatch warning — deliberately amber, distinct
5299
+ from any future genuinely-blocking calculation error, which should use a red style instead. */
5300
+ .field-cell.calc-warning {
5301
+ box-shadow: inset 0 0 0 2px var(--jupiter-warning-color, #ff9800);
5302
+ }
5303
+
5304
+ .calc-warning-badge {
5305
+ position: absolute;
5306
+ top: 2px;
5307
+ right: 2px;
5308
+ width: 16px;
5309
+ height: 16px;
5310
+ border-radius: 50%;
5311
+ border: none;
5312
+ background: var(--jupiter-warning-color, #ff9800);
5313
+ color: #fff;
5314
+ font-size: 10px;
5315
+ font-weight: 700;
5316
+ line-height: 1;
5317
+ padding: 0;
5318
+ display: flex;
5319
+ align-items: center;
5320
+ justify-content: center;
5321
+ cursor: pointer;
5322
+ z-index: 3;
5323
+ }
5324
+
5325
+ .calc-warning-badge:hover {
5326
+ background: #e65100;
5197
5327
  }
5198
5328
 
5199
5329
  /* Row-level highlight while a field in this row has focus. Applied to every
@@ -5324,7 +5454,7 @@ __decorateClass$5([
5324
5454
  ], JupiterConceptTree.prototype, "highlightColumnId", 2);
5325
5455
  __decorateClass$5([
5326
5456
  n2({ type: Object })
5327
- ], JupiterConceptTree.prototype, "calculationErrorKeys", 2);
5457
+ ], JupiterConceptTree.prototype, "calculationMismatches", 2);
5328
5458
  __decorateClass$5([
5329
5459
  n2({ type: String })
5330
5460
  ], JupiterConceptTree.prototype, "language", 2);
@@ -6029,12 +6159,18 @@ let JupiterFormSection = class extends LitElement {
6029
6159
  this._boundFieldBlur = (e2) => {
6030
6160
  this._clearHighlights();
6031
6161
  this._focusedConceptId = null;
6162
+ if (this.mode === "readonly")
6163
+ return;
6164
+ const detail = e2.detail;
6165
+ if ((detail == null ? void 0 : detail.conceptId) && (detail == null ? void 0 : detail.columnId)) {
6166
+ this._runCalculationCheck(detail.conceptId, detail.columnId);
6167
+ }
6032
6168
  };
6033
6169
  this._expandedConcepts = /* @__PURE__ */ new Set();
6034
6170
  this._allTreeExpanded = false;
6035
6171
  this._highlightMap = /* @__PURE__ */ new Map();
6036
6172
  this._focusedConceptId = null;
6037
- this._calculationErrorKeys = /* @__PURE__ */ new Set();
6173
+ this._calculationMismatches = /* @__PURE__ */ new Map();
6038
6174
  this._totalChildrenMap = /* @__PURE__ */ new Map();
6039
6175
  this._memberParentMap = /* @__PURE__ */ new Map();
6040
6176
  this._totalConceptMap = /* @__PURE__ */ new Map();
@@ -6342,50 +6478,100 @@ let JupiterFormSection = class extends LitElement {
6342
6478
  return;
6343
6479
  const totalConcept = this._totalConceptMap.get(totalConceptId);
6344
6480
  const totalRaw = (_a = this.formData[totalConceptId]) == null ? void 0 : _a[columnId];
6345
- const errorKey = `${totalConceptId}__${columnId}`;
6481
+ const key = `${totalConceptId}__${columnId}`;
6346
6482
  if (totalRaw === null || totalRaw === void 0 || totalRaw === "") {
6347
- if (this._calculationErrorKeys.has(errorKey)) {
6348
- const next2 = new Set(this._calculationErrorKeys);
6349
- next2.delete(errorKey);
6350
- this._calculationErrorKeys = next2;
6351
- }
6483
+ this._clearCalculationMismatch(key);
6352
6484
  return;
6353
6485
  }
6354
6486
  const totalValue = parseFloat(totalRaw);
6355
- if (isNaN(totalValue))
6487
+ if (isNaN(totalValue)) {
6488
+ this._clearCalculationMismatch(key);
6356
6489
  return;
6490
+ }
6357
6491
  const children = this._totalChildConceptsMap.get(totalConceptId) || [];
6358
- const childData = children.map((child) => {
6359
- var _a2;
6360
- return {
6361
- value: (_a2 = this.formData[child.id]) == null ? void 0 : _a2[columnId],
6362
- balance: child.balance
6363
- };
6364
- });
6365
- const isValid = JupiterFormSection._checkTotal(totalValue, childData, totalConcept.balance);
6366
- const next = new Set(this._calculationErrorKeys);
6367
- if (!isValid) {
6368
- next.add(errorKey);
6369
- } else {
6370
- next.delete(errorKey);
6492
+ const { calculatedSum, hasAnyValue, breakdown } = JupiterFormSection._buildCalculationBreakdown(
6493
+ children,
6494
+ this.formData,
6495
+ columnId,
6496
+ totalConcept.balance
6497
+ );
6498
+ if (!hasAnyValue || JupiterFormSection._checkTotal(totalValue, calculatedSum)) {
6499
+ this._clearCalculationMismatch(key);
6500
+ return;
6371
6501
  }
6372
- this._calculationErrorKeys = next;
6502
+ const detailWithoutMessage = {
6503
+ totalConceptId,
6504
+ totalLabel: totalConcept.label,
6505
+ columnId,
6506
+ sectionId: this.section.id,
6507
+ enteredTotal: totalValue,
6508
+ calculatedSum,
6509
+ children: breakdown
6510
+ };
6511
+ const detail = {
6512
+ ...detailWithoutMessage,
6513
+ message: JupiterFormSection._buildMismatchMessage(detailWithoutMessage)
6514
+ };
6515
+ const next = new Map(this._calculationMismatches);
6516
+ next.set(key, detail);
6517
+ this._calculationMismatches = next;
6518
+ this._dispatchCalculationMismatchChanged(key, detail);
6519
+ }
6520
+ _clearCalculationMismatch(key) {
6521
+ if (!this._calculationMismatches.has(key))
6522
+ return;
6523
+ const next = new Map(this._calculationMismatches);
6524
+ next.delete(key);
6525
+ this._calculationMismatches = next;
6526
+ this._dispatchCalculationMismatchChanged(key, null);
6527
+ }
6528
+ _dispatchCalculationMismatchChanged(key, mismatch) {
6529
+ this.dispatchEvent(new CustomEvent("calculation-mismatch-changed", {
6530
+ detail: { key, mismatch },
6531
+ bubbles: true,
6532
+ composed: true
6533
+ }));
6534
+ }
6535
+ /** Single source of truth for the debit/credit weighting used both to validate the total and to build the breakdown shown to the user. */
6536
+ static _resolveWeight(totalBalance, childBalance) {
6537
+ return !totalBalance || !childBalance || totalBalance === childBalance ? 1 : -1;
6373
6538
  }
6374
- static _checkTotal(totalValue, children, totalBalance) {
6539
+ static _buildCalculationBreakdown(children, formData, columnId, totalBalance) {
6540
+ var _a;
6375
6541
  let calculatedSum = 0;
6376
6542
  let hasAnyValue = false;
6543
+ const breakdown = [];
6377
6544
  for (const child of children) {
6378
- const val = parseFloat(child.value);
6545
+ const val = parseFloat((_a = formData[child.id]) == null ? void 0 : _a[columnId]);
6379
6546
  if (isNaN(val))
6380
6547
  continue;
6381
6548
  hasAnyValue = true;
6382
- const weight = !totalBalance || !child.balance || totalBalance === child.balance ? 1 : -1;
6549
+ const weight = JupiterFormSection._resolveWeight(totalBalance, child.balance);
6383
6550
  calculatedSum += weight * val;
6551
+ breakdown.push({ conceptId: child.id, label: child.label, value: val, balance: child.balance, weight });
6384
6552
  }
6385
- if (!hasAnyValue)
6386
- return true;
6553
+ return { calculatedSum, hasAnyValue, breakdown };
6554
+ }
6555
+ static _checkTotal(totalValue, calculatedSum) {
6387
6556
  return Math.abs(calculatedSum - totalValue) < 0.01;
6388
6557
  }
6558
+ static _formatNumber(n3) {
6559
+ return Number.isInteger(n3) ? String(n3) : n3.toFixed(2);
6560
+ }
6561
+ static _buildMismatchMessage(detail) {
6562
+ const summary = I18n.t("calculationWarning.summaryLine", {
6563
+ label: detail.totalLabel,
6564
+ calculated: JupiterFormSection._formatNumber(detail.calculatedSum),
6565
+ entered: JupiterFormSection._formatNumber(detail.enteredTotal)
6566
+ });
6567
+ if (detail.children.length === 0)
6568
+ return summary;
6569
+ const childLines = detail.children.map((child) => I18n.t(
6570
+ child.weight === 1 ? "calculationWarning.childAdded" : "calculationWarning.childSubtracted",
6571
+ { label: child.label, value: JupiterFormSection._formatNumber(child.value) }
6572
+ ));
6573
+ return `${summary} ${I18n.t("calculationWarning.childrenPrefix")} ${childLines.join("; ")}`;
6574
+ }
6389
6575
  _handleFieldFocusForHighlight(event) {
6390
6576
  var _a, _b;
6391
6577
  const conceptId = (_a = event.detail) == null ? void 0 : _a.conceptId;
@@ -6481,7 +6667,7 @@ let JupiterFormSection = class extends LitElement {
6481
6667
  .highlightType="${(_a2 = this._highlightMap.get(instanceConcept.id)) == null ? void 0 : _a2.type}"
6482
6668
  .highlightColumnId="${(_b = this._highlightMap.get(instanceConcept.id)) == null ? void 0 : _b.columnId}"
6483
6669
  .rowFocused="${this._focusedConceptId === instanceConcept.id}"
6484
- .calculationErrorKeys="${this._calculationErrorKeys}"
6670
+ .calculationMismatches="${this._calculationMismatches}"
6485
6671
  @field-change="${this._handleFieldChange}"
6486
6672
  @field-focus="${this._handleFieldFocusForHighlight}"
6487
6673
  @period-change="${this._handlePeriodChange}"
@@ -7054,7 +7240,7 @@ __decorateClass$3([
7054
7240
  ], JupiterFormSection.prototype, "_focusedConceptId", 2);
7055
7241
  __decorateClass$3([
7056
7242
  r()
7057
- ], JupiterFormSection.prototype, "_calculationErrorKeys", 2);
7243
+ ], JupiterFormSection.prototype, "_calculationMismatches", 2);
7058
7244
  JupiterFormSection = __decorateClass$3([
7059
7245
  t$1("jupiter-form-section")
7060
7246
  ], JupiterFormSection);
@@ -9097,6 +9283,7 @@ let JupiterDynamicForm = class extends LitElement {
9097
9283
  this._valid = true;
9098
9284
  this._submitted = false;
9099
9285
  this._xbrlFormErrors = [];
9286
+ this._calculationWarnings = /* @__PURE__ */ new Map();
9100
9287
  this._showErrorPopup = false;
9101
9288
  this._submitDisabled = false;
9102
9289
  this._allSections = [];
@@ -9144,6 +9331,9 @@ let JupiterDynamicForm = class extends LitElement {
9144
9331
  const customEvent = e2;
9145
9332
  this._handleFieldBlur(customEvent);
9146
9333
  });
9334
+ this.addEventListener("calculation-mismatch-changed", (e2) => {
9335
+ this._handleCalculationMismatchChanged(e2);
9336
+ });
9147
9337
  document.addEventListener("click", () => {
9148
9338
  if (this._showRoleContextMenu) {
9149
9339
  this._showRoleContextMenu = false;
@@ -9931,9 +10121,29 @@ let JupiterDynamicForm = class extends LitElement {
9931
10121
  }
9932
10122
  }
9933
10123
  }
10124
+ errors.push(...Array.from(this._calculationWarnings.values()));
9934
10125
  this._errors = errors;
9935
10126
  this._valid = errors.filter((e2) => e2.severity === "error").length === 0;
9936
10127
  }
10128
+ _handleCalculationMismatchChanged(event) {
10129
+ const { key, mismatch } = event.detail;
10130
+ const next = new Map(this._calculationWarnings);
10131
+ if (mismatch) {
10132
+ next.set(key, {
10133
+ fieldId: `${mismatch.totalConceptId}__${mismatch.columnId}`,
10134
+ conceptId: mismatch.totalConceptId,
10135
+ columnId: mismatch.columnId,
10136
+ message: mismatch.message,
10137
+ severity: "warning",
10138
+ rule: { type: "custom", message: mismatch.message, severity: "warning" },
10139
+ details: mismatch
10140
+ });
10141
+ } else {
10142
+ next.delete(key);
10143
+ }
10144
+ this._calculationWarnings = next;
10145
+ this._validateForm();
10146
+ }
9937
10147
  _handleFieldChange(event) {
9938
10148
  const { fieldId, conceptId, columnId, value } = event.detail;
9939
10149
  console.log(`📝 Field change: conceptId=${conceptId}, columnId=${columnId}, value=${value}, fieldId=${fieldId}`);
@@ -10547,6 +10757,52 @@ let JupiterDynamicForm = class extends LitElement {
10547
10757
  }
10548
10758
  return instantDate || endDate || startDate || "2025-01-01";
10549
10759
  }
10760
+ /**
10761
+ * JDF-037: Self-heals legacy periodStartLabel instant dates.
10762
+ * Drafts saved before JDF-024 have periodStartLabel concepts with the instant date
10763
+ * set to the column's own year instead of one day before periodStartDate. Reuses
10764
+ * _resolveInstantDate (no new date math) to correct both _periodData and the live
10765
+ * field.periodInstantDate so UI and saved output never disagree. Idempotent: only
10766
+ * writes when the stored value actually differs from the corrected one.
10767
+ */
10768
+ _correctLegacyPeriodStartInstantDates() {
10769
+ this._allSections.forEach((section2) => {
10770
+ this._correctPeriodStartInstantDatesInConcepts(section2.concepts, section2);
10771
+ });
10772
+ }
10773
+ _correctPeriodStartInstantDatesInConcepts(concepts, section2) {
10774
+ concepts.forEach((concept) => {
10775
+ var _a, _b;
10776
+ if (((_a = concept.preferredLabel) == null ? void 0 : _a.includes("periodStartLabel")) && concept.periodType === "instant" && ((_b = concept.fields) == null ? void 0 : _b.length)) {
10777
+ concept.fields.forEach((field2) => {
10778
+ this._correctFieldPeriodStartInstantDate(concept, field2, concept.id, section2);
10779
+ const repeatCount = this._repeatCounts[concept.id] || 0;
10780
+ for (let n3 = 1; n3 <= repeatCount; n3++) {
10781
+ this._correctFieldPeriodStartInstantDate(concept, field2, `${concept.id}__repeat_${n3}`, section2);
10782
+ }
10783
+ });
10784
+ }
10785
+ if (concept.children) {
10786
+ this._correctPeriodStartInstantDatesInConcepts(concept.children, section2);
10787
+ }
10788
+ });
10789
+ }
10790
+ _correctFieldPeriodStartInstantDate(concept, field2, periodDataKey, section2) {
10791
+ var _a;
10792
+ const column2 = this._findColumnByIdInSection(field2.columnId, section2);
10793
+ const fieldPeriodData = (_a = this._periodData[periodDataKey]) == null ? void 0 : _a[field2.columnId];
10794
+ const periodStartDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
10795
+ const periodEndDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
10796
+ const correctedInstantDate = this._resolveInstantDate(concept.preferredLabel, void 0, periodStartDate, periodEndDate);
10797
+ if (fieldPeriodData && fieldPeriodData.instantDate !== correctedInstantDate) {
10798
+ console.log(`🩹 [Legacy Period Correction] ${periodDataKey}/${field2.columnId}: instantDate ${fieldPeriodData.instantDate} → ${correctedInstantDate}`);
10799
+ fieldPeriodData.instantDate = correctedInstantDate;
10800
+ }
10801
+ if (field2.periodInstantDate !== correctedInstantDate) {
10802
+ console.log(`🩹 [Legacy Period Correction] field ${concept.id}/${field2.columnId}: periodInstantDate ${field2.periodInstantDate} → ${correctedInstantDate}`);
10803
+ field2.periodInstantDate = correctedInstantDate;
10804
+ }
10805
+ }
10550
10806
  _addColumnFromRequest(request, sectionId, insertAfterColumnId) {
10551
10807
  var _a, _b, _c, _d, _e;
10552
10808
  const timestamp = Date.now();
@@ -10846,6 +11102,7 @@ let JupiterDynamicForm = class extends LitElement {
10846
11102
  this.requestUpdate();
10847
11103
  return;
10848
11104
  }
11105
+ this._correctLegacyPeriodStartInstantDates();
10849
11106
  const submissionData = this._generateSubmissionData();
10850
11107
  console.log("📊 Form Submission Data:", JSON.stringify(submissionData, null, 2));
10851
11108
  console.log("📊 Submission Data Summary:");
@@ -10881,6 +11138,7 @@ let JupiterDynamicForm = class extends LitElement {
10881
11138
  console.log("💾 Raw form data before submission generation:", this._formData);
10882
11139
  console.log("🏷️ Unit data before submission generation:", this._unitData);
10883
11140
  console.log("📋 Enhanced selectedRoleIds with roleURI and order:", this._selectedRoleIds);
11141
+ this._correctLegacyPeriodStartInstantDates();
10884
11142
  const draftData = this._generateSubmissionData();
10885
11143
  console.log("📤 Generated submission data:", draftData);
10886
11144
  console.log("📊 Submission data breakdown:");
@@ -11139,6 +11397,7 @@ let JupiterDynamicForm = class extends LitElement {
11139
11397
  };
11140
11398
  console.log(`🏷️ [Unit Restore] Merged unit data: ${Object.keys(this._unitData).length} concepts with custom units`);
11141
11399
  console.log(`🏷️ [Unit Restore] Full _unitData after merge:`, JSON.stringify(this._unitData, null, 2));
11400
+ this._correctLegacyPeriodStartInstantDates();
11142
11401
  if (this._selectedRoleIds.length > 0) {
11143
11402
  this._applyRoleFilter();
11144
11403
  }
@@ -12645,11 +12904,11 @@ let JupiterDynamicForm = class extends LitElement {
12645
12904
  <div class="form-sections">
12646
12905
  <!-- Validation Summary -->
12647
12906
  ${showValidationSummary ? html`
12648
- <div class="validation-summary">
12649
- <h4 class="validation-summary-title">${I18n.t("validation.summary")}</h4>
12907
+ <div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
12908
+ <h4 class="validation-summary-title">${errorCount > 0 ? I18n.t("validation.summary") : I18n.t("calculationWarning.title")}</h4>
12650
12909
  <ul class="validation-summary-list">
12651
- ${this._errors.filter((e2) => e2.severity === "error").map((error2) => html`
12652
- <li class="validation-summary-item">${error2.message}</li>
12910
+ ${this._errors.filter((e2) => e2.severity === "error" || e2.severity === "warning" && this.mode !== "readonly").map((error2) => html`
12911
+ <li class="validation-summary-item ${error2.severity}">${error2.severity === "warning" ? "⚠️ " : ""}${error2.message}</li>
12653
12912
  `)}
12654
12913
  </ul>
12655
12914
  </div>
@@ -12809,11 +13068,11 @@ let JupiterDynamicForm = class extends LitElement {
12809
13068
  <div class="form-sections">
12810
13069
  <!-- Validation Summary -->
12811
13070
  ${showValidationSummary ? html`
12812
- <div class="validation-summary">
12813
- <h4 class="validation-summary-title">Please fix the following errors:</h4>
13071
+ <div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
13072
+ <h4 class="validation-summary-title">${errorCount > 0 ? I18n.t("validation.summary") : I18n.t("calculationWarning.title")}</h4>
12814
13073
  <ul class="validation-summary-list">
12815
- ${this._errors.filter((e2) => e2.severity === "error").map((error2) => html`
12816
- <li class="validation-summary-item">${error2.message}</li>
13074
+ ${this._errors.filter((e2) => e2.severity === "error" || e2.severity === "warning" && this.mode !== "readonly").map((error2) => html`
13075
+ <li class="validation-summary-item ${error2.severity}">${error2.severity === "warning" ? "⚠️ " : ""}${error2.message}</li>
12817
13076
  `)}
12818
13077
  </ul>
12819
13078
  </div>
@@ -13163,8 +13422,9 @@ let JupiterDynamicForm = class extends LitElement {
13163
13422
  render() {
13164
13423
  var _a;
13165
13424
  const errorCount = this._errors.filter((e2) => e2.severity === "error").length;
13425
+ const warningCount = this.mode === "readonly" ? 0 : this._errors.filter((e2) => e2.severity === "warning").length;
13166
13426
  const config = this.config || {};
13167
- const showValidationSummary = config.showValidationSummary !== false && errorCount > 0 && this._submitted;
13427
+ const showValidationSummary = config.showValidationSummary !== false && (errorCount > 0 && this._submitted || warningCount > 0);
13168
13428
  const schema = this._currentSchema;
13169
13429
  if (!schema) {
13170
13430
  return html`<div>${I18n.t("form.loading")}</div>`;
@@ -13429,6 +13689,21 @@ JupiterDynamicForm.styles = css`
13429
13689
  margin-bottom: 4px;
13430
13690
  }
13431
13691
 
13692
+ /* JDF-036: when the summary contains only non-blocking calculation warnings (no blocking
13693
+ errors), style the whole box amber rather than error-red so it doesn't read as blocking. */
13694
+ .validation-summary.warnings-only {
13695
+ background: var(--jupiter-warning-background, #fff3e0);
13696
+ border-color: var(--jupiter-warning-color, #ff9800);
13697
+ }
13698
+
13699
+ .validation-summary.warnings-only .validation-summary-title {
13700
+ color: var(--jupiter-warning-color, #ff9800);
13701
+ }
13702
+
13703
+ .validation-summary-item.warning {
13704
+ color: var(--jupiter-warning-color, #ff9800);
13705
+ }
13706
+
13432
13707
  .form-actions button {
13433
13708
  padding: 10px 20px;
13434
13709
  border: none;
@@ -14279,6 +14554,9 @@ __decorateClass([
14279
14554
  __decorateClass([
14280
14555
  r()
14281
14556
  ], JupiterDynamicForm.prototype, "_xbrlFormErrors", 2);
14557
+ __decorateClass([
14558
+ r()
14559
+ ], JupiterDynamicForm.prototype, "_calculationWarnings", 2);
14282
14560
  __decorateClass([
14283
14561
  r()
14284
14562
  ], JupiterDynamicForm.prototype, "_showErrorPopup", 2);