jupiter-dynamic-forms 1.19.7 → 1.19.8

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
+ }));
6373
6534
  }
6374
- static _checkTotal(totalValue, children, totalBalance) {
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;
6538
+ }
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}`);
@@ -12645,11 +12855,11 @@ let JupiterDynamicForm = class extends LitElement {
12645
12855
  <div class="form-sections">
12646
12856
  <!-- Validation Summary -->
12647
12857
  ${showValidationSummary ? html`
12648
- <div class="validation-summary">
12649
- <h4 class="validation-summary-title">${I18n.t("validation.summary")}</h4>
12858
+ <div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
12859
+ <h4 class="validation-summary-title">${errorCount > 0 ? I18n.t("validation.summary") : I18n.t("calculationWarning.title")}</h4>
12650
12860
  <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>
12861
+ ${this._errors.filter((e2) => e2.severity === "error" || e2.severity === "warning" && this.mode !== "readonly").map((error2) => html`
12862
+ <li class="validation-summary-item ${error2.severity}">${error2.severity === "warning" ? "⚠️ " : ""}${error2.message}</li>
12653
12863
  `)}
12654
12864
  </ul>
12655
12865
  </div>
@@ -12809,11 +13019,11 @@ let JupiterDynamicForm = class extends LitElement {
12809
13019
  <div class="form-sections">
12810
13020
  <!-- Validation Summary -->
12811
13021
  ${showValidationSummary ? html`
12812
- <div class="validation-summary">
12813
- <h4 class="validation-summary-title">Please fix the following errors:</h4>
13022
+ <div class="validation-summary ${errorCount === 0 ? "warnings-only" : ""}">
13023
+ <h4 class="validation-summary-title">${errorCount > 0 ? I18n.t("validation.summary") : I18n.t("calculationWarning.title")}</h4>
12814
13024
  <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>
13025
+ ${this._errors.filter((e2) => e2.severity === "error" || e2.severity === "warning" && this.mode !== "readonly").map((error2) => html`
13026
+ <li class="validation-summary-item ${error2.severity}">${error2.severity === "warning" ? "⚠️ " : ""}${error2.message}</li>
12817
13027
  `)}
12818
13028
  </ul>
12819
13029
  </div>
@@ -13163,8 +13373,9 @@ let JupiterDynamicForm = class extends LitElement {
13163
13373
  render() {
13164
13374
  var _a;
13165
13375
  const errorCount = this._errors.filter((e2) => e2.severity === "error").length;
13376
+ const warningCount = this.mode === "readonly" ? 0 : this._errors.filter((e2) => e2.severity === "warning").length;
13166
13377
  const config = this.config || {};
13167
- const showValidationSummary = config.showValidationSummary !== false && errorCount > 0 && this._submitted;
13378
+ const showValidationSummary = config.showValidationSummary !== false && (errorCount > 0 && this._submitted || warningCount > 0);
13168
13379
  const schema = this._currentSchema;
13169
13380
  if (!schema) {
13170
13381
  return html`<div>${I18n.t("form.loading")}</div>`;
@@ -13429,6 +13640,21 @@ JupiterDynamicForm.styles = css`
13429
13640
  margin-bottom: 4px;
13430
13641
  }
13431
13642
 
13643
+ /* JDF-036: when the summary contains only non-blocking calculation warnings (no blocking
13644
+ errors), style the whole box amber rather than error-red so it doesn't read as blocking. */
13645
+ .validation-summary.warnings-only {
13646
+ background: var(--jupiter-warning-background, #fff3e0);
13647
+ border-color: var(--jupiter-warning-color, #ff9800);
13648
+ }
13649
+
13650
+ .validation-summary.warnings-only .validation-summary-title {
13651
+ color: var(--jupiter-warning-color, #ff9800);
13652
+ }
13653
+
13654
+ .validation-summary-item.warning {
13655
+ color: var(--jupiter-warning-color, #ff9800);
13656
+ }
13657
+
13432
13658
  .form-actions button {
13433
13659
  padding: 10px 20px;
13434
13660
  border: none;
@@ -14279,6 +14505,9 @@ __decorateClass([
14279
14505
  __decorateClass([
14280
14506
  r()
14281
14507
  ], JupiterDynamicForm.prototype, "_xbrlFormErrors", 2);
14508
+ __decorateClass([
14509
+ r()
14510
+ ], JupiterDynamicForm.prototype, "_calculationWarnings", 2);
14282
14511
  __decorateClass([
14283
14512
  r()
14284
14513
  ], JupiterDynamicForm.prototype, "_showErrorPopup", 2);