jupiter-dynamic-forms 1.17.6 → 1.17.7

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
@@ -627,9 +627,10 @@ class XBRLFormBuilder {
627
627
  const availableColumnIds = columns.map((col) => col.id);
628
628
  const roleInfo = { periodTypes, availableColumnIds, availableColumns: columns };
629
629
  const conceptTrees = [];
630
+ const conceptIdOccurrences = /* @__PURE__ */ new Map();
630
631
  if ((_a = role.presentationLinkbase) == null ? void 0 : _a.concepts) {
631
632
  role.presentationLinkbase.concepts.forEach((concept) => {
632
- const conceptTree = this.buildConceptTree(concept, 0, periodStartDate, periodEndDate, roleInfo, role.id, language);
633
+ const conceptTree = this.buildConceptTree(concept, 0, periodStartDate, periodEndDate, roleInfo, role.id, language, conceptIdOccurrences);
633
634
  if (conceptTree) {
634
635
  conceptTrees.push(conceptTree);
635
636
  }
@@ -663,8 +664,10 @@ class XBRLFormBuilder {
663
664
  /**
664
665
  * Build concept tree from XBRL presentation concept
665
666
  */
666
- static buildConceptTree(concept, level, periodStartDate, periodEndDate, roleInfo, sectionId, language = "en") {
667
+ static buildConceptTree(concept, level, periodStartDate, periodEndDate, roleInfo, sectionId, language = "en", conceptIdOccurrences, ancestorPeriodHint) {
668
+ var _a, _b;
667
669
  const label = this.getPreferredLabel(concept.labels, language);
670
+ const childPeriodHint = ((_a = concept.preferredLabel) == null ? void 0 : _a.includes("periodStartLabel")) ? "start" : ((_b = concept.preferredLabel) == null ? void 0 : _b.includes("periodEndLabel")) ? "end" : ancestorPeriodHint;
668
671
  const fields = [];
669
672
  if (!concept.elementAbstract) {
670
673
  let columnIds = [];
@@ -674,11 +677,11 @@ class XBRLFormBuilder {
674
677
  columnIds = ["duration"];
675
678
  }
676
679
  columnIds.forEach((columnId) => {
677
- var _a;
678
- const column2 = (_a = roleInfo == null ? void 0 : roleInfo.availableColumns) == null ? void 0 : _a.find((c2) => c2.id === columnId);
680
+ var _a2;
681
+ const column2 = (_a2 = roleInfo == null ? void 0 : roleInfo.availableColumns) == null ? void 0 : _a2.find((c2) => c2.id === columnId);
679
682
  const colStartDate = (column2 == null ? void 0 : column2.periodStartDate) || periodStartDate;
680
683
  const colEndDate = (column2 == null ? void 0 : column2.periodEndDate) || periodEndDate;
681
- const field2 = this.createFieldFromConcept(concept, colStartDate, colEndDate, columnId);
684
+ const field2 = this.createFieldFromConcept(concept, colStartDate, colEndDate, columnId, ancestorPeriodHint);
682
685
  if (field2)
683
686
  fields.push(field2);
684
687
  });
@@ -686,7 +689,7 @@ class XBRLFormBuilder {
686
689
  const children = [];
687
690
  if (concept.children && concept.children.length > 0) {
688
691
  concept.children.forEach((child) => {
689
- const childTree = this.buildConceptTree(child, level + 1, periodStartDate, periodEndDate, roleInfo, sectionId, language);
692
+ const childTree = this.buildConceptTree(child, level + 1, periodStartDate, periodEndDate, roleInfo, sectionId, language, conceptIdOccurrences, childPeriodHint);
690
693
  if (childTree) {
691
694
  children.push(childTree);
692
695
  }
@@ -696,9 +699,16 @@ class XBRLFormBuilder {
696
699
  if (!hasContent) {
697
700
  return null;
698
701
  }
702
+ let conceptId = this.createUniqueConceptId(concept, sectionId);
703
+ if (conceptIdOccurrences) {
704
+ const count = conceptIdOccurrences.get(conceptId) ?? 0;
705
+ conceptIdOccurrences.set(conceptId, count + 1);
706
+ if (count > 0) {
707
+ conceptId = `${conceptId}__occ${count + 1}`;
708
+ }
709
+ }
699
710
  return {
700
- id: this.createUniqueConceptId(concept, sectionId),
701
- // Use unique ID for form management
711
+ id: conceptId,
702
712
  originalConceptId: concept.id,
703
713
  // Store original for submission data
704
714
  name: concept.conceptName,
@@ -724,7 +734,7 @@ class XBRLFormBuilder {
724
734
  /**
725
735
  * Create form field from XBRL concept
726
736
  */
727
- static createFieldFromConcept(concept, periodStartDate, periodEndDate, forcedColumnId) {
737
+ static createFieldFromConcept(concept, periodStartDate, periodEndDate, forcedColumnId, ancestorPeriodHint) {
728
738
  var _a;
729
739
  const fieldType = this.mapXBRLTypeToFieldType(concept.type);
730
740
  const label = this.getPreferredLabel(concept.labels);
@@ -750,7 +760,7 @@ class XBRLFormBuilder {
750
760
  // Validate and set the period type
751
761
  periodStartDate: periodStartDate || "2025-01-01",
752
762
  periodEndDate: periodEndDate || "2025-12-31",
753
- periodInstantDate: concept.periodType === "instant" ? ((_a = concept.preferredLabel) == null ? void 0 : _a.includes("periodStartLabel")) ? this.subtractOneDay(periodStartDate || "2025-01-01") : periodEndDate || periodStartDate || "2025-01-01" : void 0
763
+ periodInstantDate: concept.periodType === "instant" ? ((_a = concept.preferredLabel) == null ? void 0 : _a.includes("periodStartLabel")) || ancestorPeriodHint === "start" ? this.subtractOneDay(periodStartDate || "2025-01-01") : periodEndDate || periodStartDate || "2025-01-01" : void 0
754
764
  };
755
765
  if (concept.id === "nl-cd_DescriptionLocationNL__a64trl") {
756
766
  console.log(`đŸ—ī¸ [Field Creation] Concept: ${concept.id}, periodType from concept: ${concept.periodType}, Field periodType: ${field2.periodType}, ColumnId: ${columnId}`);
@@ -2139,7 +2149,7 @@ class DraftStorageService {
2139
2149
  /**
2140
2150
  * Create metadata snapshot from current form state
2141
2151
  */
2142
- createMetadataSnapshot(periodStartDate, periodEndDate, language, selectedRoleIds, allSections, typedMemberData, periodPreferences, periodData, unitData, reportingLanguage = "en", repeatCounts, decimalsData) {
2152
+ createMetadataSnapshot(periodStartDate, periodEndDate, language, selectedRoleIds, allSections, typedMemberData, periodPreferences, periodData, unitData, reportingLanguage = "en", repeatCounts, decimalsData, roleCompletedStates) {
2143
2153
  return {
2144
2154
  periodStartDate,
2145
2155
  periodEndDate,
@@ -2154,6 +2164,7 @@ class DraftStorageService {
2154
2164
  unitData,
2155
2165
  decimalsData,
2156
2166
  repeatCounts,
2167
+ roleCompletedStates,
2157
2168
  schemaVersion: this.STORAGE_VERSION
2158
2169
  };
2159
2170
  }
@@ -8203,6 +8214,7 @@ let JupiterDynamicForm = class extends LitElement {
8203
8214
  this._sidePanelCollapsed = false;
8204
8215
  this._adminRoleConfigs = {};
8205
8216
  this._roleBorderStatuses = /* @__PURE__ */ new Map();
8217
+ this._roleCompletedStates = /* @__PURE__ */ new Map();
8206
8218
  this._showRoleContextMenu = false;
8207
8219
  this._contextMenuX = 0;
8208
8220
  this._contextMenuY = 0;
@@ -9921,6 +9933,7 @@ let JupiterDynamicForm = class extends LitElement {
9921
9933
  draftData.forEach((entry, index) => {
9922
9934
  console.log(` [${index}] conceptId: ${entry.conceptId}, columnId: ${entry.columnId}, value: ${entry.value}, unit: ${entry.unit || "none"}`);
9923
9935
  });
9936
+ const roleCompletedStates = this._roleCompletedStates.size > 0 ? Object.fromEntries(this._roleCompletedStates) : void 0;
9924
9937
  const metadata = this._draftStorageService.createMetadataSnapshot(
9925
9938
  this.periodStartDate,
9926
9939
  this.periodEndDate,
@@ -9934,7 +9947,8 @@ let JupiterDynamicForm = class extends LitElement {
9934
9947
  this._unitData,
9935
9948
  this.reportingLanguage,
9936
9949
  this._repeatCounts,
9937
- this._decimalsData
9950
+ this._decimalsData,
9951
+ roleCompletedStates
9938
9952
  );
9939
9953
  const draftPayloadSnapshot = JSON.stringify({
9940
9954
  draftData,
@@ -10064,6 +10078,10 @@ let JupiterDynamicForm = class extends LitElement {
10064
10078
  this._decimalsData = metadata.decimalsData;
10065
10079
  console.log("🔄 Restored per-fact decimals data:", Object.keys(this._decimalsData).length, "concepts");
10066
10080
  }
10081
+ if (metadata.roleCompletedStates) {
10082
+ this._roleCompletedStates = new Map(Object.entries(metadata.roleCompletedStates));
10083
+ console.log("🔄 Restored role completed states:", Object.keys(metadata.roleCompletedStates).length, "roles");
10084
+ }
10067
10085
  if (metadata.periodPreferences) {
10068
10086
  if (this._skipPeriodPreferencesRestore) {
10069
10087
  console.log("â­ī¸ Skipping period preferences restoration - using new filter selections");
@@ -11065,6 +11083,15 @@ let JupiterDynamicForm = class extends LitElement {
11065
11083
  console.log(`â„šī¸ User can re-add this role using the Filter Roles dialog`);
11066
11084
  this._closeRoleContextMenu();
11067
11085
  }
11086
+ _handleRoleCompletedChange(roleId, checked) {
11087
+ if (checked) {
11088
+ this._roleCompletedStates.set(roleId, true);
11089
+ } else {
11090
+ this._roleCompletedStates.delete(roleId);
11091
+ }
11092
+ this._roleCompletedStates = new Map(this._roleCompletedStates);
11093
+ this._handleSaveDraft("auto");
11094
+ }
11068
11095
  _handleSidePanelRoleClick(roleId) {
11069
11096
  if (this._activeSidePanelRoleId && this._activeSidePanelRoleId !== roleId) {
11070
11097
  this._analyzeAndLogRole(this._activeSidePanelRoleId);
@@ -11102,6 +11129,10 @@ let JupiterDynamicForm = class extends LitElement {
11102
11129
  result = "INVALID";
11103
11130
  console.warn(`
11104
11131
  🔴 RESULT: INVALID (contains validation errors)`);
11132
+ } else if (filledCount === 0) {
11133
+ result = "UNTOUCHED";
11134
+ console.warn(`
11135
+ âŦœ RESULT: UNTOUCHED (no data entered)`);
11105
11136
  } else if (emptyCount > 0) {
11106
11137
  result = "INCOMPLETE";
11107
11138
  console.warn(`
@@ -11115,7 +11146,10 @@ let JupiterDynamicForm = class extends LitElement {
11115
11146
  `);
11116
11147
  if (result === "INVALID")
11117
11148
  ;
11118
- else if (result === "INCOMPLETE") {
11149
+ else if (result === "UNTOUCHED") {
11150
+ this._roleBorderStatuses.set(roleId, "untouched");
11151
+ this._roleBorderStatuses = new Map(this._roleBorderStatuses);
11152
+ } else if (result === "INCOMPLETE") {
11119
11153
  this._roleBorderStatuses.set(roleId, "incomplete");
11120
11154
  this._roleBorderStatuses = new Map(this._roleBorderStatuses);
11121
11155
  } else {
@@ -11777,7 +11811,8 @@ let JupiterDynamicForm = class extends LitElement {
11777
11811
  ${filteredSections.map((section2) => {
11778
11812
  const hasErrors = this._roleHasErrors(section2.id);
11779
11813
  const borderStatus = this._roleBorderStatuses.get(section2.id);
11780
- const statusClass = hasErrors ? "has-errors" : borderStatus === "incomplete" ? "has-empty-fields" : borderStatus === "complete" ? "has-complete-data" : "";
11814
+ const isManuallyCompleted = this._roleCompletedStates.get(section2.id) === true;
11815
+ const statusClass = isManuallyCompleted ? "has-complete-data" : hasErrors ? "has-errors" : borderStatus === "untouched" ? "has-no-data" : borderStatus === "incomplete" ? "has-empty-fields" : borderStatus === "complete" ? "has-complete-data" : "";
11781
11816
  return html`
11782
11817
  <div
11783
11818
  class="side-panel-role-item ${section2.id === this._activeSidePanelRoleId ? "active" : ""} ${statusClass}"
@@ -11847,6 +11882,15 @@ let JupiterDynamicForm = class extends LitElement {
11847
11882
  @column-remove="${this._handleColumnRemove}"
11848
11883
  @column-add-request="${this._handleColumnAddRequest}"
11849
11884
  ></jupiter-form-section>
11885
+ <div class="role-completion-row">
11886
+ <input
11887
+ type="checkbox"
11888
+ id="role-complete-${activeSection.id}"
11889
+ .checked="${this._roleCompletedStates.get(activeSection.id) === true}"
11890
+ @change="${(e2) => this._handleRoleCompletedChange(activeSection.id, e2.target.checked)}"
11891
+ >
11892
+ <label for="role-complete-${activeSection.id}">Mark as completed</label>
11893
+ </div>
11850
11894
  ` : html`
11851
11895
  <div class="no-roles-message">
11852
11896
  <h3>${I18n.t("form.noRoleSelected")}</h3>
@@ -12472,6 +12516,14 @@ JupiterDynamicForm.styles = css`
12472
12516
  border-left: 2px solid var(--jupiter-error-color, #d32f2f);
12473
12517
  }
12474
12518
 
12519
+ .side-panel-role-item.has-no-data {
12520
+ border-left: 2px solid var(--jupiter-untouched-color, #757575);
12521
+ }
12522
+
12523
+ .side-panel-role-item.has-no-data.active {
12524
+ border-left: 2px solid var(--jupiter-untouched-color, #757575);
12525
+ }
12526
+
12475
12527
  .side-panel-role-item.has-empty-fields {
12476
12528
  border-left: 2px solid var(--jupiter-warning-color, #ff9800);
12477
12529
  }
@@ -12488,6 +12540,31 @@ JupiterDynamicForm.styles = css`
12488
12540
  border-left: 2px solid var(--jupiter-success-color, #4caf50);
12489
12541
  }
12490
12542
 
12543
+ .role-completion-row {
12544
+ display: flex;
12545
+ align-items: center;
12546
+ gap: 8px;
12547
+ padding: 12px 16px;
12548
+ border-top: 1px solid var(--jupiter-border-color, #e0e0e0);
12549
+ margin-top: 4px;
12550
+ background: var(--jupiter-background, #fff);
12551
+ }
12552
+
12553
+ .role-completion-row input[type="checkbox"] {
12554
+ width: 16px;
12555
+ height: 16px;
12556
+ cursor: pointer;
12557
+ accent-color: var(--jupiter-success-color, #4caf50);
12558
+ flex-shrink: 0;
12559
+ }
12560
+
12561
+ .role-completion-row label {
12562
+ font-size: 14px;
12563
+ color: var(--jupiter-text-secondary, #555);
12564
+ cursor: pointer;
12565
+ user-select: none;
12566
+ }
12567
+
12491
12568
  .side-panel-content {
12492
12569
  flex: 1;
12493
12570
  overflow-y: auto;
@@ -12983,6 +13060,9 @@ __decorateClass([
12983
13060
  __decorateClass([
12984
13061
  r()
12985
13062
  ], JupiterDynamicForm.prototype, "_roleBorderStatuses", 2);
13063
+ __decorateClass([
13064
+ r()
13065
+ ], JupiterDynamicForm.prototype, "_roleCompletedStates", 2);
12986
13066
  __decorateClass([
12987
13067
  r()
12988
13068
  ], JupiterDynamicForm.prototype, "_showRoleContextMenu", 2);