jupiter-dynamic-forms 1.17.5 → 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
  }
@@ -3064,6 +3075,9 @@ let JupiterFormField = class extends LitElement {
3064
3075
  );
3065
3076
  }
3066
3077
  }
3078
+ _isRoundingLevelConcept() {
3079
+ return !!this.conceptId && this.conceptId.includes("DocumentIntendedRoundingLevel");
3080
+ }
3067
3081
  _handleInput(event) {
3068
3082
  const target = event.target;
3069
3083
  let value = target.value;
@@ -3235,6 +3249,9 @@ let JupiterFormField = class extends LitElement {
3235
3249
  console.log(`❌ [FormField] Unit validation failed for ${this.conceptId}: unit is required but not selected`);
3236
3250
  }
3237
3251
  _handlePeriodIconClick() {
3252
+ if (this._isRoundingLevelConcept()) {
3253
+ return;
3254
+ }
3238
3255
  this._availableUnits = this._collectUnitsForConceptType();
3239
3256
  console.log(`📊 [FormField] Collected ${this._availableUnits.length} units for concept ${this.conceptId}:`, this._availableUnits);
3240
3257
  console.log(`📊 [FormField] Current unit value: ${this.unit}`);
@@ -3688,7 +3705,7 @@ let JupiterFormField = class extends LitElement {
3688
3705
  min="0"
3689
3706
  class="typed-member-input"
3690
3707
  placeholder="Leave blank for INF"
3691
- .value="${this.decimals ?? (this.globalDecimals !== "INF" ? this.globalDecimals : "")}"
3708
+ .value="${this.decimals !== void 0 && this.decimals !== null && this.decimals !== "" ? this.decimals : this.globalDecimals !== "INF" ? this.globalDecimals : ""}"
3692
3709
  @input="${this._handleDecimalsChange}"
3693
3710
  ?disabled="${this.disabled}"
3694
3711
  />
@@ -3724,6 +3741,7 @@ let JupiterFormField = class extends LitElement {
3724
3741
  const hasPeriodControl = this.field.periodType && (this.field.periodType === "instant" || this.field.periodType === "duration");
3725
3742
  const baseConceptId = this._extractBaseConceptId(this.conceptId);
3726
3743
  const isPredefinedValue = this.mode !== "admin" && this.masterData && baseConceptId in this.masterData;
3744
+ const isRoundingLevelLocked = this._isRoundingLevelConcept();
3727
3745
  let factValue = null;
3728
3746
  if (this.facts && this.facts.length > 0 && !isPredefinedValue) {
3729
3747
  const cellContext = {
@@ -3778,7 +3796,7 @@ let JupiterFormField = class extends LitElement {
3778
3796
  }
3779
3797
  const hasUserValue = this.value !== null && this.value !== void 0;
3780
3798
  const effectiveValue = isPredefinedValue ? this.masterData[baseConceptId] : hasUserValue ? this.value : factValue;
3781
- const effectiveDisabled = isPredefinedValue || this.disabled || this.mode === "readonly";
3799
+ const effectiveDisabled = isPredefinedValue || isRoundingLevelLocked || this.disabled || this.mode === "readonly";
3782
3800
  return html`
3783
3801
  <div class="field-container">
3784
3802
  ${showLabel ? html`
@@ -3790,7 +3808,7 @@ let JupiterFormField = class extends LitElement {
3790
3808
  <div class="field-wrapper">
3791
3809
  ${this._renderInput(effectiveValue, effectiveDisabled)}
3792
3810
 
3793
- ${hasPeriodControl && this.mode !== "readonly" ? html`
3811
+ ${hasPeriodControl && this.mode !== "readonly" && !isRoundingLevelLocked ? html`
3794
3812
  <button
3795
3813
  class="period-icon-btn"
3796
3814
  type="button"
@@ -8196,6 +8214,7 @@ let JupiterDynamicForm = class extends LitElement {
8196
8214
  this._sidePanelCollapsed = false;
8197
8215
  this._adminRoleConfigs = {};
8198
8216
  this._roleBorderStatuses = /* @__PURE__ */ new Map();
8217
+ this._roleCompletedStates = /* @__PURE__ */ new Map();
8199
8218
  this._showRoleContextMenu = false;
8200
8219
  this._contextMenuX = 0;
8201
8220
  this._contextMenuY = 0;
@@ -8380,6 +8399,7 @@ let JupiterDynamicForm = class extends LitElement {
8380
8399
  if ((_a = this.xbrlInput) == null ? void 0 : _a.initialData) {
8381
8400
  this._formData = { ...this._formData, ...this.xbrlInput.initialData };
8382
8401
  }
8402
+ this._initializeGlobalDecimalsFromRoundingData();
8383
8403
  this._extractTypedMembersFromFacts();
8384
8404
  if (!this._skipDraftLoading) {
8385
8405
  this._loadDraftIfExists().then(() => {
@@ -9100,6 +9120,107 @@ let JupiterDynamicForm = class extends LitElement {
9100
9120
  }
9101
9121
  this._effectiveMasterData = this.masterData ? { ...this.masterData } : void 0;
9102
9122
  }
9123
+ _initializeGlobalDecimalsFromRoundingData() {
9124
+ if (this._hasValidGlobalDecimals()) {
9125
+ return;
9126
+ }
9127
+ const roundingLevel = this._findRoundingLevelFactValue(this._allSections) ?? this._findRoundingLevelValueInFormData(this._formData);
9128
+ if (roundingLevel === void 0 || roundingLevel === null || roundingLevel === "") {
9129
+ return;
9130
+ }
9131
+ const parsed = parseFloat(String(roundingLevel));
9132
+ if (isNaN(parsed)) {
9133
+ return;
9134
+ }
9135
+ this.decimals = String(Math.abs(parsed));
9136
+ }
9137
+ _findRoundingLevelValueInFormData(formData) {
9138
+ if (!formData) {
9139
+ return void 0;
9140
+ }
9141
+ for (const conceptKey of Object.keys(formData)) {
9142
+ if (!this._isRoundingLevelConcept(conceptKey)) {
9143
+ continue;
9144
+ }
9145
+ const columnValues = formData[conceptKey] || {};
9146
+ for (const columnId of Object.keys(columnValues)) {
9147
+ const value = columnValues[columnId];
9148
+ if (value !== void 0 && value !== null && value !== "") {
9149
+ return value;
9150
+ }
9151
+ }
9152
+ }
9153
+ return void 0;
9154
+ }
9155
+ _findRoundingLevelFactValue(sections) {
9156
+ for (const section2 of sections) {
9157
+ const value = this._findRoundingLevelFactValueInConcepts(section2.concepts || []);
9158
+ if (value !== void 0 && value !== null && value !== "") {
9159
+ return value;
9160
+ }
9161
+ }
9162
+ return void 0;
9163
+ }
9164
+ _findRoundingLevelFactValueInConcepts(concepts) {
9165
+ for (const concept of concepts) {
9166
+ const conceptIdToCheck = concept.originalConceptId || concept.id;
9167
+ if (this._isRoundingLevelConcept(conceptIdToCheck)) {
9168
+ const facts = concept.facts || [];
9169
+ for (const fact of facts) {
9170
+ if ((fact == null ? void 0 : fact.value) !== void 0 && (fact == null ? void 0 : fact.value) !== null && (fact == null ? void 0 : fact.value) !== "") {
9171
+ return fact.value;
9172
+ }
9173
+ }
9174
+ }
9175
+ if (concept.children && concept.children.length > 0) {
9176
+ const childValue = this._findRoundingLevelFactValueInConcepts(concept.children);
9177
+ if (childValue !== void 0 && childValue !== null && childValue !== "") {
9178
+ return childValue;
9179
+ }
9180
+ }
9181
+ }
9182
+ return void 0;
9183
+ }
9184
+ _hasValidGlobalDecimals() {
9185
+ if (this.decimals === void 0 || this.decimals === null) {
9186
+ return false;
9187
+ }
9188
+ if (this.decimals === "INF") {
9189
+ return false;
9190
+ }
9191
+ const parsed = parseFloat(this.decimals);
9192
+ return !isNaN(parsed);
9193
+ }
9194
+ _isRoundingLevelConcept(conceptId) {
9195
+ if (!conceptId) {
9196
+ return false;
9197
+ }
9198
+ return conceptId === "rj-i_DocumentIntendedRoundingLevel" || conceptId.includes("DocumentIntendedRoundingLevel");
9199
+ }
9200
+ _findFactValueForField(concept, field2, section2) {
9201
+ var _a, _b;
9202
+ if (!concept.facts || concept.facts.length === 0) {
9203
+ return void 0;
9204
+ }
9205
+ const column2 = this._findColumnByIdInSection(field2.columnId, section2);
9206
+ const fieldPeriodData = (_a = this._periodData[concept.id]) == null ? void 0 : _a[field2.columnId];
9207
+ const periodStartDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.startDate) || (column2 == null ? void 0 : column2.periodStartDate) || field2.periodStartDate || this.periodStartDate;
9208
+ const periodEndDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.endDate) || (column2 == null ? void 0 : column2.periodEndDate) || field2.periodEndDate || this.periodEndDate;
9209
+ const periodInstantDate = (fieldPeriodData == null ? void 0 : fieldPeriodData.instantDate) || field2.periodInstantDate || periodEndDate || periodStartDate;
9210
+ const unit = (_b = this._unitData[concept.id]) == null ? void 0 : _b[field2.columnId];
9211
+ const cellContext = {
9212
+ conceptId: concept.originalConceptId || field2.conceptId || concept.id,
9213
+ columnId: field2.columnId,
9214
+ periodStartDate,
9215
+ periodEndDate,
9216
+ periodInstantDate,
9217
+ periodType: field2.periodType,
9218
+ unit,
9219
+ dimensionData: column2 == null ? void 0 : column2.dimensionData
9220
+ };
9221
+ const matchedFact = FactMatcher.findMatchingFact(concept.facts, cellContext);
9222
+ return matchedFact == null ? void 0 : matchedFact.value;
9223
+ }
9103
9224
  _storeDecimals(conceptId, columnId, decimals) {
9104
9225
  const updated = { ...this._decimalsData };
9105
9226
  if (!updated[conceptId])
@@ -9812,6 +9933,7 @@ let JupiterDynamicForm = class extends LitElement {
9812
9933
  draftData.forEach((entry, index) => {
9813
9934
  console.log(` [${index}] conceptId: ${entry.conceptId}, columnId: ${entry.columnId}, value: ${entry.value}, unit: ${entry.unit || "none"}`);
9814
9935
  });
9936
+ const roleCompletedStates = this._roleCompletedStates.size > 0 ? Object.fromEntries(this._roleCompletedStates) : void 0;
9815
9937
  const metadata = this._draftStorageService.createMetadataSnapshot(
9816
9938
  this.periodStartDate,
9817
9939
  this.periodEndDate,
@@ -9825,7 +9947,8 @@ let JupiterDynamicForm = class extends LitElement {
9825
9947
  this._unitData,
9826
9948
  this.reportingLanguage,
9827
9949
  this._repeatCounts,
9828
- this._decimalsData
9950
+ this._decimalsData,
9951
+ roleCompletedStates
9829
9952
  );
9830
9953
  const draftPayloadSnapshot = JSON.stringify({
9831
9954
  draftData,
@@ -9955,6 +10078,10 @@ let JupiterDynamicForm = class extends LitElement {
9955
10078
  this._decimalsData = metadata.decimalsData;
9956
10079
  console.log("🔄 Restored per-fact decimals data:", Object.keys(this._decimalsData).length, "concepts");
9957
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
+ }
9958
10085
  if (metadata.periodPreferences) {
9959
10086
  if (this._skipPeriodPreferencesRestore) {
9960
10087
  console.log("â­ī¸ Skipping period preferences restoration - using new filter selections");
@@ -10034,6 +10161,7 @@ let JupiterDynamicForm = class extends LitElement {
10034
10161
  });
10035
10162
  this._formData = { ...this._formData, ...restoredFormData };
10036
10163
  console.log(`🔄 Restored ${Object.keys(restoredFormData).length} concepts with data`);
10164
+ this._initializeGlobalDecimalsFromRoundingData();
10037
10165
  this._periodData = {
10038
10166
  ...this._periodData,
10039
10167
  ...metadata.periodData || {},
@@ -10672,8 +10800,14 @@ let JupiterDynamicForm = class extends LitElement {
10672
10800
  var _a, _b, _c, _d, _e, _f, _g, _h;
10673
10801
  const conceptData = this._formData[concept.id];
10674
10802
  let fieldValue = conceptData == null ? void 0 : conceptData[field2.columnId];
10803
+ const baseConceptId = field2.conceptId || concept.id.split("__").slice(0, -1).join("__") || concept.id;
10804
+ if ((fieldValue === void 0 || fieldValue === null || fieldValue === "") && this._isRoundingLevelConcept(baseConceptId) && !this._hasValidGlobalDecimals()) {
10805
+ const factValue = this._findFactValueForField(concept, field2, section2);
10806
+ if (factValue !== void 0 && factValue !== null && factValue !== "") {
10807
+ fieldValue = factValue;
10808
+ }
10809
+ }
10675
10810
  if ((fieldValue === void 0 || fieldValue === null || fieldValue === "") && this._effectiveMasterData) {
10676
- const baseConceptId = field2.conceptId || concept.id.split("__").slice(0, -1).join("__") || concept.id;
10677
10811
  const masterValue = (_a = this._effectiveMasterData) == null ? void 0 : _a[baseConceptId];
10678
10812
  if (masterValue !== void 0 && masterValue !== null && masterValue !== "") {
10679
10813
  fieldValue = masterValue;
@@ -10949,6 +11083,15 @@ let JupiterDynamicForm = class extends LitElement {
10949
11083
  console.log(`â„šī¸ User can re-add this role using the Filter Roles dialog`);
10950
11084
  this._closeRoleContextMenu();
10951
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
+ }
10952
11095
  _handleSidePanelRoleClick(roleId) {
10953
11096
  if (this._activeSidePanelRoleId && this._activeSidePanelRoleId !== roleId) {
10954
11097
  this._analyzeAndLogRole(this._activeSidePanelRoleId);
@@ -10986,6 +11129,10 @@ let JupiterDynamicForm = class extends LitElement {
10986
11129
  result = "INVALID";
10987
11130
  console.warn(`
10988
11131
  🔴 RESULT: INVALID (contains validation errors)`);
11132
+ } else if (filledCount === 0) {
11133
+ result = "UNTOUCHED";
11134
+ console.warn(`
11135
+ âŦœ RESULT: UNTOUCHED (no data entered)`);
10989
11136
  } else if (emptyCount > 0) {
10990
11137
  result = "INCOMPLETE";
10991
11138
  console.warn(`
@@ -10999,7 +11146,10 @@ let JupiterDynamicForm = class extends LitElement {
10999
11146
  `);
11000
11147
  if (result === "INVALID")
11001
11148
  ;
11002
- 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") {
11003
11153
  this._roleBorderStatuses.set(roleId, "incomplete");
11004
11154
  this._roleBorderStatuses = new Map(this._roleBorderStatuses);
11005
11155
  } else {
@@ -11661,7 +11811,8 @@ let JupiterDynamicForm = class extends LitElement {
11661
11811
  ${filteredSections.map((section2) => {
11662
11812
  const hasErrors = this._roleHasErrors(section2.id);
11663
11813
  const borderStatus = this._roleBorderStatuses.get(section2.id);
11664
- 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" : "";
11665
11816
  return html`
11666
11817
  <div
11667
11818
  class="side-panel-role-item ${section2.id === this._activeSidePanelRoleId ? "active" : ""} ${statusClass}"
@@ -11731,6 +11882,15 @@ let JupiterDynamicForm = class extends LitElement {
11731
11882
  @column-remove="${this._handleColumnRemove}"
11732
11883
  @column-add-request="${this._handleColumnAddRequest}"
11733
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>
11734
11894
  ` : html`
11735
11895
  <div class="no-roles-message">
11736
11896
  <h3>${I18n.t("form.noRoleSelected")}</h3>
@@ -12356,6 +12516,14 @@ JupiterDynamicForm.styles = css`
12356
12516
  border-left: 2px solid var(--jupiter-error-color, #d32f2f);
12357
12517
  }
12358
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
+
12359
12527
  .side-panel-role-item.has-empty-fields {
12360
12528
  border-left: 2px solid var(--jupiter-warning-color, #ff9800);
12361
12529
  }
@@ -12372,6 +12540,31 @@ JupiterDynamicForm.styles = css`
12372
12540
  border-left: 2px solid var(--jupiter-success-color, #4caf50);
12373
12541
  }
12374
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
+
12375
12568
  .side-panel-content {
12376
12569
  flex: 1;
12377
12570
  overflow-y: auto;
@@ -12867,6 +13060,9 @@ __decorateClass([
12867
13060
  __decorateClass([
12868
13061
  r()
12869
13062
  ], JupiterDynamicForm.prototype, "_roleBorderStatuses", 2);
13063
+ __decorateClass([
13064
+ r()
13065
+ ], JupiterDynamicForm.prototype, "_roleCompletedStates", 2);
12870
13066
  __decorateClass([
12871
13067
  r()
12872
13068
  ], JupiterDynamicForm.prototype, "_showRoleContextMenu", 2);