@sapui5/sap.suite.ui.generic.template 1.151.1 → 1.153.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 (87) hide show
  1. package/package.json +1 -1
  2. package/src/sap/suite/ui/generic/template/.library +1 -1
  3. package/src/sap/suite/ui/generic/template/AnalyticalListPage/control/visualfilterbar/SmartVisualFilterBar.js +4 -3
  4. package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
  5. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  6. package/src/sap/suite/ui/generic/template/ListReport/Component.js +4 -2
  7. package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +0 -6
  8. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_sv.properties +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ListReport/view/fragments/FullscreenPage.fragment.xml +3 -0
  11. package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +5 -0
  12. package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +31 -0
  13. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  14. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +1 -1
  15. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  16. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  17. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +13 -7
  18. package/src/sap/suite/ui/generic/template/genericUtilities/designtime/messagebundle.properties +0 -0
  19. package/src/sap/suite/ui/generic/template/js/AnnotationHelper.js +21 -17
  20. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +19 -3
  21. package/src/sap/suite/ui/generic/template/lib/ShareUtils.js +1 -1
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +16 -6
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +35 -47
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +80 -64
  25. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +442 -259
  26. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +186 -161
  27. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +137 -161
  28. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +157 -191
  29. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +67 -85
  30. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +56 -4
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -2
  32. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +7 -1
  33. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +7 -1
  34. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +7 -1
  35. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +7 -1
  36. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +7 -1
  37. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +7 -1
  38. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +7 -1
  39. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +7 -1
  40. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +7 -1
  41. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +7 -1
  42. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +7 -1
  43. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_sappsd.properties +7 -1
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +7 -1
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saptrc.properties +13 -7
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +7 -1
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +7 -1
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +7 -1
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +11 -5
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +10 -4
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +7 -1
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +7 -1
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +7 -1
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +7 -1
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +7 -1
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +7 -1
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +7 -1
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +7 -1
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +7 -1
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +7 -1
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +7 -1
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +7 -1
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +7 -1
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +7 -1
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +7 -1
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +7 -1
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +7 -1
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +7 -1
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +7 -1
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +7 -1
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +7 -1
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +7 -1
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +7 -1
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +8 -2
  75. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +7 -1
  76. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +7 -1
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +7 -1
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +7 -1
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +7 -1
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +7 -1
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +7 -1
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +7 -1
  83. package/src/sap/suite/ui/generic/template/lib/insights/InsightsCardHelper.js +40 -8
  84. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +3 -2
  85. package/src/sap/suite/ui/generic/template/lib/presentationControl/SmartTableHandler.js +4 -0
  86. package/src/sap/suite/ui/generic/template/library.js +1 -1
  87. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +7 -1
@@ -2,10 +2,11 @@ sap.ui.define([
2
2
  "sap/ui/comp/smartform/GroupElement",
3
3
  "sap/ui/comp/smartfield/SmartField",
4
4
  "sap/m/Text",
5
+ "sap/m/Link",
6
+ "sap/m/Button",
5
7
  "sap/ui/layout/form/ColumnElementData",
6
- "sap/suite/ui/generic/template/genericUtilities/FeLogger",
7
- "sap/suite/ui/generic/template/genericUtilities/FeError"
8
- ], function(GroupElement, SmartField, Text, ColumnElementData, FeLogger, FeError) {
8
+ "sap/suite/ui/generic/template/genericUtilities/FeLogger"
9
+ ], function(GroupElement, SmartField, Text, Link, Button, ColumnElementData, FeLogger) {
9
10
  "use strict";
10
11
 
11
12
  var sClassName = "lib.ai.EasyFill.EasyFillFormRenderer";
@@ -28,103 +29,101 @@ sap.ui.define([
28
29
  return {
29
30
 
30
31
  /**
31
- * Populates a target SmartForm group with GroupElements for the given list of field names.
32
- * For each field:
33
- * - Creates an editable SmartField bound to the transient context (if updatable)
34
- * alongside a read-only "Previously:" SmartField bound to the original OP context.
35
- * - Creates a display-only SmartField bound to the OP context (if non-updatable).
32
+ * Collects all SmartForm controls from an ObjectPageLayout.
33
+ * In condensed mode the SmartForm is nested inside a wrapper VBox; this helper
34
+ * handles that one-level-deep wrapping transparently.
35
+ * Extracted as a shared helper so EasyFillHandler no longer duplicates this walk.
36
36
  *
37
- * This function is called asynchronously (via setTimeout) to allow the SmartForm DOM to render
38
- * before GroupElements are injected.
39
- *
40
- * @param {string} sId - the CustomData "id" value identifying the target SmartForm group
41
- * @param {Array<string>} aFields - array of property names to render as SmartFields
42
- * @param {string} sEasyFillMode - current layout mode (Condensed / ObjectPage) — for future use
43
- * @param {object} oController - MVC controller (used to byId the ObjectPageLayout)
44
- * @param {object} oTransientContextForEasyFill - transient OData context for AI-suggested values
45
- * @param {object} oObjectPageContext - original ObjectPage binding context (current values)
46
- * @param {object} mUpdatableLookupMap - map of { fieldName: value } for updatable fields only
47
- * @param {object} oLLMData - flat map of { fieldName: aiValue } from the LLM response
48
- * @returns {boolean} always returns true (used as a formatter return value in XML)
37
+ * @param {object} oObjectPageLayout - sap.uxap.ObjectPageLayout
38
+ * @returns {Array<sap.ui.comp.smartform.SmartForm>}
49
39
  */
50
- populateSmartFields: function(sId, aFields, sEasyFillMode, oController, oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oLLMData, fnOnFieldChange) {
51
- var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
52
- if (!oObjectPageLayout || !Array.isArray(aFields) || aFields.length === 0) {
53
- return true;
54
- }
55
-
56
- var sPreviouslyLabel = oController.getView().getModel("i18n").getResourceBundle().getText("EASY_FILL_PREVIOUSLY_LABEL");
57
-
58
- // Defer injection until after the SmartForm has rendered into the DOM
59
- setTimeout(function() {
60
- var oTargetGroup = null;
61
-
62
- // Walk the ObjectPageLayout section hierarchy to find the group
63
- // matching the given CustomData "id" value.
64
- // In condensed mode the SmartForm may be nested inside a wrapper VBox
65
- // (used to prevent BlockLayout from placing the table side-by-side with
66
- // the SmartForm on resize). We therefore check one level deeper when the
67
- // direct block is not a SmartForm.
68
- oObjectPageLayout.getSections().forEach(function(oSection) {
69
- oSection.getSubSections().forEach(function(oSubSection) {
70
- oSubSection.getBlocks().forEach(function(oBlock) {
71
- var aSmartFormCandidates = [];
72
- if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
73
- aSmartFormCandidates.push(oBlock);
74
- } else if (typeof oBlock.getItems === "function") {
75
- // Look one level inside (e.g. condensed-mode wrapper VBox)
76
- oBlock.getItems().forEach(function(oItem) {
77
- if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
78
- aSmartFormCandidates.push(oItem);
79
- }
80
- });
81
- }
82
- aSmartFormCandidates.forEach(function(oSmartForm) {
83
- oSmartForm.getGroups().forEach(function(oGroup) {
84
- var oMatchedCustomData = oGroup.getCustomData().find(function(oData) {
85
- return oData.getKey() === "id" && oData.getValue() === sId;
86
- });
87
- if (oMatchedCustomData) {
88
- oTargetGroup = oGroup;
89
- }
90
- });
40
+ collectSmartForms: function(oObjectPageLayout) {
41
+ var aForms = [];
42
+ if (!oObjectPageLayout) { return aForms; }
43
+ oObjectPageLayout.getSections().forEach(function(oSection) {
44
+ oSection.getSubSections().forEach(function(oSubSection) {
45
+ oSubSection.getBlocks().forEach(function(oBlock) {
46
+ if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
47
+ aForms.push(oBlock);
48
+ } else if (typeof oBlock.getItems === "function") {
49
+ oBlock.getItems().forEach(function(oItem) {
50
+ if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
51
+ aForms.push(oItem);
52
+ }
91
53
  });
92
- });
54
+ }
93
55
  });
94
56
  });
57
+ });
58
+ return aForms;
59
+ },
95
60
 
96
- if (!oTargetGroup) {
97
- oLogger.warning("populateSmartFields: no group found with id '" + sId + "'");
98
- return;
99
- }
61
+ /**
62
+ * Finds a SmartForm.Group by its CustomData "id" value within an ObjectPageLayout.
63
+ * Returns null if not found. O(groups) — linear in the number of groups across all forms.
64
+ *
65
+ * @param {object} oObjectPageLayout - sap.uxap.ObjectPageLayout
66
+ * @param {string} sId - the CustomData "id" value to match
67
+ * @returns {sap.ui.comp.smartform.Group|null}
68
+ */
69
+ findGroupById: function(oObjectPageLayout, sId) {
70
+ var oFound = null;
71
+ this.collectSmartForms(oObjectPageLayout).some(function(oSmartForm) {
72
+ return oSmartForm.getGroups().some(function(oGroup) {
73
+ var bMatch = oGroup.getCustomData().some(function(oData) {
74
+ return oData.getKey() === "id" && oData.getValue() === sId;
75
+ });
76
+ if (bMatch) { oFound = oGroup; }
77
+ return bMatch;
78
+ });
79
+ });
80
+ return oFound;
81
+ },
82
+
83
+ /**
84
+ * Populates a SmartForm.Group (looked up by CustomData "id" walk via findGroupById) with
85
+ * GroupElements containing dual SmartFields. Called DIRECTLY by EasyFillHandler
86
+ * after the LLM result arrives — not via a formatter, not via setTimeout.
87
+ *
88
+ * The group is created by the sections= JSONModel binding in the XML fragment.
89
+ * If the group is not yet in the DOM when called, an afterRendering delegate on the
90
+ * OPL retries injection once the binding has created the controls.
91
+ *
92
+ * @param {string} sGroupId - the CustomData "id" value of the target SmartForm.Group
93
+ * @param {Array} aFields - field names to render
94
+ * @param {object} oController - MVC controller
95
+ * @param {object} oTransientContextForEasyFill
96
+ * @param {object} oObjectPageContext
97
+ * @param {object} mUpdatableLookupMap - { fieldName: aiValue } updatable only
98
+ * @param {object} oLLMData - { fieldName: aiValue } full LLM result
99
+ * @param {function} fnOnFieldChange
100
+ */
101
+ populateGroupFields: function(sGroupId, aFields, oController, oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oLLMData, fnOnFieldChange) {
102
+ if (!Array.isArray(aFields) || aFields.length === 0) { return; }
100
103
 
101
- // Clear any previously rendered GroupElements before re-populating
102
- oTargetGroup.destroyGroupElements();
104
+ var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
105
+ var oRb = oController.getView().getModel("i18n").getResourceBundle();
106
+ var sOriginalValueLabel = oRb.getText("EASY_FILL_ORIGINAL_VALUE_LABEL");
107
+ var sProposedValueLabel = oRb.getText("EASY_FILL_PROPOSED_VALUE_LABEL");
108
+ var sRevertTooltip = oRb.getText("EASY_FILL_REVERT_FIELD_TOOLTIP");
109
+ var that = this;
110
+
111
+ function tryInject() {
112
+ var oGroup = that.findGroupById(oObjectPageLayout, sGroupId);
113
+ if (!oGroup || oGroup.bIsDestroyed) { return false; }
114
+
115
+ oGroup.destroyGroupElements();
103
116
 
104
117
  aFields.forEach(function(sKey) {
105
118
  var bIsEditable = Object.prototype.hasOwnProperty.call(mUpdatableLookupMap || {}, sKey);
106
119
 
107
- var oGroupElement = new GroupElement({
108
- useHorizontalLayout: false
109
- });
110
-
111
- /**
112
- * Primary SmartField:
113
- * - If editable: bound to transient context, shows AI-suggested value
114
- * - If non-editable: bound to OP context, display-only
115
- */
116
120
  var oSmartField = new SmartField({
117
121
  value: "{" + sKey + "}",
118
122
  editable: bIsEditable,
119
123
  enabled: bIsEditable,
120
124
  contextEditable: bIsEditable,
121
- layoutData: new ColumnElementData({
122
- cellsSmall: 12,
123
- cellsLarge: 12
124
- }),
125
+ layoutData: new ColumnElementData({ cellsSmall: 12, cellsLarge: 12 }),
125
126
  editableChanged: function(oEvent) {
126
- // Prevent the SmartField from toggling its own editability;
127
- // editability is controlled exclusively by EasyFill logic
128
127
  if (oEvent.getParameter("editable") !== bIsEditable) {
129
128
  oEvent.getSource().setEditable(bIsEditable);
130
129
  }
@@ -132,45 +131,17 @@ sap.ui.define([
132
131
  });
133
132
 
134
133
  if (!bIsEditable) {
135
- // Keep read-only behavior, but render like an input field (v4-like visual parity)
136
134
  oSmartField.addStyleClass("sapUiEasyFillReadOnlyInputLike sapMInputBaseContentWrapper");
137
135
  }
138
136
 
139
- // Notify on user edits so SmartForm validity can re-gate the Confirm button.
140
137
  if (bIsEditable && typeof fnOnFieldChange === "function") {
141
138
  oSmartField.attachChange(fnOnFieldChange);
142
-
143
- // Validate AI-seeded values on initial load — mirrors ObjectPage behaviour.
144
- //
145
- // In the OP, validation triggers because when a user TYPES a value the browser
146
- // fires a change event which causes UI5 to call setExternalValue() on the inner
147
- // control's binding. setExternalValue() has no fast-path: it always calls
148
- // parseValue → validateValue → OData value-list lookup. If the lookup fails the
149
- // binding calls setInvalidValue() + checkDataState() → ManagedObject fires
150
- // validationError → inner control gets valueState="Error" (red mark).
151
- //
152
- // SmartForm.check() / SmartField.checkValuesValidity() do NOT work for
153
- // AI-seeded values because SmartField._checkErrors() contains a fast-path:
154
- // if (!isControlDirty() && formattedModelValue === displayedValue) → skip
155
- // AI values always satisfy this (model = display = AI value, user never typed),
156
- // so _checkErrors() short-circuits to "valid" without the OData lookup.
157
- //
158
- // Fix: after innerControlsCreated, call setExternalValue() on the inner
159
- // control's own binding with the currently displayed value. This is exactly
160
- // the same path the OP uses when the user types. For async types (TextArrangement
161
- // with ValueList) the call starts the OData lookup:
162
- // valid value → resolves quietly, no red mark
163
- // invalid value → rejects → setInvalidValue + checkDataState → red mark
164
- // Non-async types fall through to a plain fnOnFieldChange call because
165
- // SmartForm.check() already handles them correctly (no fast-path for sync).
166
139
  oSmartField.attachInnerControlsCreated(function() {
167
140
  var oInnerControl = oSmartField.getFirstInnerControl();
168
141
  if (oInnerControl && typeof oInnerControl.getValue === "function") {
169
142
  var oBinding = oInnerControl.getBinding("value");
170
143
  var oType = oBinding && typeof oBinding.getType === "function" && oBinding.getType();
171
144
  if (oType && oType.async && typeof oBinding.setExternalValue === "function") {
172
- // setExternalValue replicates the "user typed this value" path:
173
- // parseValue → validateValue → OData lookup → validationError/Success
174
145
  Promise.resolve(oBinding.setExternalValue(oInnerControl.getValue()))
175
146
  .catch(Function.prototype)
176
147
  .finally(fnOnFieldChange);
@@ -181,69 +152,123 @@ sap.ui.define([
181
152
  });
182
153
  }
183
154
 
184
- /**
185
- * "Previously:" SmartField (read-only):
186
- * - Only shown for editable fields to display the current OP value for comparison
187
- * - Bound to the original ObjectPage context
188
- */
189
- var oSmartFieldPrev = new SmartField({
190
- value: "{" + sKey + "}",
191
- editable: false,
192
- enabled: false,
193
- contextEditable: false,
194
- showLabel: false,
195
- layoutData: new ColumnElementData({
196
- cellsSmall: 9,
197
- cellsLarge: 9
198
- }),
199
- editableChanged: function(oEvent) {
200
- // Always keep the "previously" field read-only
201
- if (oEvent.getParameter("editable") !== false) {
202
- oEvent.getSource().setEditable(false);
203
- }
204
- }
205
- });
155
+ // Hoist vAIValue so it can be reused when seeding the transient context below
156
+ var vAIValue = oLLMData[sKey];
206
157
 
207
158
  if (oTransientContextForEasyFill) {
208
- // Primary field always uses transient context to show AI-proposed value.
209
- // Field editability is controlled by bIsEditable.
210
159
  oSmartField.setBindingContext(oTransientContextForEasyFill);
211
-
212
- // Only seed the transient context with the AI-suggested value on the first
213
- // population. On subsequent calls (e.g. mode switch), the transient context
214
- // already holds the current value — overwriting it would discard user edits.
215
160
  if (oTransientContextForEasyFill.getProperty(sKey) === undefined) {
216
- oTransientContextForEasyFill.setProperty(sKey, oLLMData[sKey]);
161
+ oTransientContextForEasyFill.setProperty(sKey, vAIValue);
217
162
  }
218
163
  } else {
219
- // Fallback: without transient context, bind to original OP context.
220
164
  oSmartField.setBindingContext(oObjectPageContext);
221
165
  }
222
166
 
223
- // Previous value is always shown from the original ObjectPage context.
224
- oSmartFieldPrev.setBindingContext(oObjectPageContext);
167
+ // Read the current (pre-AI) value as a plain string for display.
168
+ var vCurrentValue = (oObjectPageContext && typeof oObjectPageContext.getProperty === "function")
169
+ ? oObjectPageContext.getProperty(sKey) : null;
170
+ var sCurrentValue;
171
+ if (vCurrentValue === null || vCurrentValue === undefined || vCurrentValue === "") {
172
+ sCurrentValue = "-";
173
+ } else if (vCurrentValue instanceof Date) {
174
+ sCurrentValue = vCurrentValue.toLocaleDateString();
175
+ } else if (typeof vCurrentValue === "object") {
176
+ sCurrentValue = "-";
177
+ } else {
178
+ sCurrentValue = String(vCurrentValue);
179
+ }
180
+
181
+ var oGroupElement = new GroupElement({ useHorizontalLayout: false });
182
+ oGroupElement.addElement(oSmartField);
183
+
184
+ // "Original Value:" text shown for all fields (editable and non-editable)
185
+ var oOriginalValueText = new Text({
186
+ text: sOriginalValueLabel + " " + sCurrentValue,
187
+ wrapping: false
188
+ }).addStyleClass("sapEasyFillPreviouslyLabel");
189
+ oGroupElement.addElement(oOriginalValueText);
225
190
 
226
- // Build the GroupElement: [label+primary SmartField] [Previously: label] [preview SmartField]
227
- oGroupElement.addElement(oSmartField); // SmartField provides the label
228
- oGroupElement.addElement(new Text({
229
- text: sPreviouslyLabel,
230
- layoutData: new ColumnElementData({
231
- cellsSmall: 3,
232
- cellsLarge: 3
233
- })
234
- }).addStyleClass("sapEasyFillPreviouslyLabel"));
235
- oGroupElement.addElement(oSmartFieldPrev);
236
- oTargetGroup.addGroupElement(oGroupElement);
191
+ // Toggle controls (revert button + proposed value link) only for editable fields
192
+ if (bIsEditable) {
193
+ var bReverted = false;
194
+
195
+ var oRevertButton = new Button({
196
+ icon: "sap-icon://undo",
197
+ type: "Transparent",
198
+ tooltip: sRevertTooltip
199
+ }).addStyleClass("sapEasyFillRevertButton");
200
+
201
+ // State 2 row: "Proposed Value: <aiValue>" clickable link — hidden until user reverts
202
+ var sAIDisplayValue;
203
+ if (vAIValue === null || vAIValue === undefined || vAIValue === "") {
204
+ sAIDisplayValue = "-";
205
+ } else if (vAIValue instanceof Date) {
206
+ sAIDisplayValue = vAIValue.toLocaleDateString();
207
+ } else if (typeof vAIValue === "object") {
208
+ sAIDisplayValue = "-";
209
+ } else {
210
+ sAIDisplayValue = String(vAIValue);
211
+ }
212
+
213
+ var oProposedValueLink = new Link({
214
+ text: sProposedValueLabel + " " + sAIDisplayValue,
215
+ wrapping: false,
216
+ visible: false,
217
+ icon: "sap-icon://ai"
218
+ }).addStyleClass("sapEasyFillProposedValueLink");
219
+
220
+ // Revert button: State 1 → State 2
221
+ oRevertButton.attachPress(function() {
222
+ if (bReverted) { return; }
223
+ bReverted = true;
224
+ if (oTransientContextForEasyFill) {
225
+ oTransientContextForEasyFill.setProperty(sKey, vCurrentValue);
226
+ }
227
+ oOriginalValueText.setVisible(false);
228
+ oRevertButton.setVisible(false);
229
+ oProposedValueLink.setVisible(true);
230
+ if (typeof fnOnFieldChange === "function") { fnOnFieldChange(); }
231
+ });
232
+
233
+ // Proposed Value link: State 2 → State 1
234
+ oProposedValueLink.attachPress(function() {
235
+ if (!bReverted) { return; }
236
+ bReverted = false;
237
+ if (oTransientContextForEasyFill) {
238
+ oTransientContextForEasyFill.setProperty(sKey, vAIValue);
239
+ }
240
+ oProposedValueLink.setVisible(false);
241
+ oOriginalValueText.setVisible(true);
242
+ oRevertButton.setVisible(true);
243
+ if (typeof fnOnFieldChange === "function") { fnOnFieldChange(); }
244
+ });
245
+
246
+ oGroupElement.addElement(oRevertButton);
247
+ oGroupElement.addElement(oProposedValueLink);
248
+ }
249
+
250
+ oGroup.addGroupElement(oGroupElement);
237
251
  });
238
252
 
239
- // Trigger initial SmartForm validation now that all SmartFields are rendered
240
- if (typeof fnOnFieldChange === "function") {
241
- fnOnFieldChange();
242
- }
243
- oLogger.debug("populateSmartFields: populated " + aFields.length + " fields for group '" + sId + "'");
244
- }, 0);
253
+ if (typeof fnOnFieldChange === "function") { fnOnFieldChange(); }
254
+ oLogger.debug("populateGroupFields: " + aFields.length + " fields in group '" + sGroupId + "'");
255
+ return true;
256
+ }
245
257
 
246
- return true;
258
+ // If the group control exists already (e.g. mode switch on existing sections),
259
+ // inject immediately. Otherwise the sections= binding hasn't created the group
260
+ // control yet — attach a one-shot afterRendering on the OPL to retry once
261
+ // the new sections are in the DOM.
262
+ if (!tryInject()) {
263
+ if (!oObjectPageLayout) { return; }
264
+ var oDelegate = {
265
+ onAfterRendering: function() {
266
+ oObjectPageLayout.removeEventDelegate(oDelegate);
267
+ tryInject();
268
+ }
269
+ };
270
+ oObjectPageLayout.addEventDelegate(oDelegate);
271
+ }
247
272
  },
248
273
 
249
274
  /**