@sapui5/sap.suite.ui.generic.template 1.151.1 → 1.152.1

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 (35) 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/i18n/i18n_sv.properties +1 -1
  8. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/view/fragments/FullscreenPage.fragment.xml +3 -0
  10. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  11. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  12. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  13. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +5 -4
  14. package/src/sap/suite/ui/generic/template/js/AnnotationHelper.js +21 -17
  15. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  16. package/src/sap/suite/ui/generic/template/lib/Application.js +0 -13
  17. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +4 -19
  18. package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +4 -4
  19. package/src/sap/suite/ui/generic/template/lib/ShareUtils.js +1 -1
  20. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +1 -6
  21. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +25 -44
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +80 -64
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +442 -259
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +125 -161
  25. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +134 -160
  26. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +55 -189
  27. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +67 -85
  28. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +27 -3
  29. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saptrc.properties +6 -6
  30. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +4 -4
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +1 -1
  32. package/src/sap/suite/ui/generic/template/lib/insights/InsightsCardHelper.js +16 -4
  33. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +3 -2
  34. package/src/sap/suite/ui/generic/template/library.js +1 -1
  35. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +1 -1
@@ -3,9 +3,8 @@ sap.ui.define([
3
3
  "sap/ui/comp/smartfield/SmartField",
4
4
  "sap/m/Text",
5
5
  "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) {
6
+ "sap/suite/ui/generic/template/genericUtilities/FeLogger"
7
+ ], function(GroupElement, SmartField, Text, ColumnElementData, FeLogger) {
9
8
  "use strict";
10
9
 
11
10
  var sClassName = "lib.ai.EasyFill.EasyFillFormRenderer";
@@ -28,103 +27,98 @@ sap.ui.define([
28
27
  return {
29
28
 
30
29
  /**
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).
30
+ * Collects all SmartForm controls from an ObjectPageLayout.
31
+ * In condensed mode the SmartForm is nested inside a wrapper VBox; this helper
32
+ * handles that one-level-deep wrapping transparently.
33
+ * Extracted as a shared helper so EasyFillHandler no longer duplicates this walk.
36
34
  *
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)
35
+ * @param {object} oObjectPageLayout - sap.uxap.ObjectPageLayout
36
+ * @returns {Array<sap.ui.comp.smartform.SmartForm>}
49
37
  */
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
- });
38
+ collectSmartForms: function(oObjectPageLayout) {
39
+ var aForms = [];
40
+ if (!oObjectPageLayout) { return aForms; }
41
+ oObjectPageLayout.getSections().forEach(function(oSection) {
42
+ oSection.getSubSections().forEach(function(oSubSection) {
43
+ oSubSection.getBlocks().forEach(function(oBlock) {
44
+ if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
45
+ aForms.push(oBlock);
46
+ } else if (typeof oBlock.getItems === "function") {
47
+ oBlock.getItems().forEach(function(oItem) {
48
+ if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
49
+ aForms.push(oItem);
50
+ }
91
51
  });
92
- });
52
+ }
93
53
  });
94
54
  });
55
+ });
56
+ return aForms;
57
+ },
95
58
 
96
- if (!oTargetGroup) {
97
- oLogger.warning("populateSmartFields: no group found with id '" + sId + "'");
98
- return;
99
- }
59
+ /**
60
+ * Finds a SmartForm.Group by its CustomData "id" value within an ObjectPageLayout.
61
+ * Returns null if not found. O(groups) — linear in the number of groups across all forms.
62
+ *
63
+ * @param {object} oObjectPageLayout - sap.uxap.ObjectPageLayout
64
+ * @param {string} sId - the CustomData "id" value to match
65
+ * @returns {sap.ui.comp.smartform.Group|null}
66
+ */
67
+ findGroupById: function(oObjectPageLayout, sId) {
68
+ var oFound = null;
69
+ this.collectSmartForms(oObjectPageLayout).some(function(oSmartForm) {
70
+ return oSmartForm.getGroups().some(function(oGroup) {
71
+ var bMatch = oGroup.getCustomData().some(function(oData) {
72
+ return oData.getKey() === "id" && oData.getValue() === sId;
73
+ });
74
+ if (bMatch) { oFound = oGroup; }
75
+ return bMatch;
76
+ });
77
+ });
78
+ return oFound;
79
+ },
100
80
 
101
- // Clear any previously rendered GroupElements before re-populating
102
- oTargetGroup.destroyGroupElements();
81
+ /**
82
+ * Populates a SmartForm.Group (looked up by CustomData "id" walk via findGroupById) with
83
+ * GroupElements containing dual SmartFields. Called DIRECTLY by EasyFillHandler
84
+ * after the LLM result arrives — not via a formatter, not via setTimeout.
85
+ *
86
+ * The group is created by the sections= JSONModel binding in the XML fragment.
87
+ * If the group is not yet in the DOM when called, an afterRendering delegate on the
88
+ * OPL retries injection once the binding has created the controls.
89
+ *
90
+ * @param {string} sGroupId - the CustomData "id" value of the target SmartForm.Group
91
+ * @param {Array} aFields - field names to render
92
+ * @param {object} oController - MVC controller
93
+ * @param {object} oTransientContextForEasyFill
94
+ * @param {object} oObjectPageContext
95
+ * @param {object} mUpdatableLookupMap - { fieldName: aiValue } updatable only
96
+ * @param {object} oLLMData - { fieldName: aiValue } full LLM result
97
+ * @param {function} fnOnFieldChange
98
+ */
99
+ populateGroupFields: function(sGroupId, aFields, oController, oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oLLMData, fnOnFieldChange) {
100
+ if (!Array.isArray(aFields) || aFields.length === 0) { return; }
101
+
102
+ var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
103
+ var sPreviouslyLabel = oController.getView().getModel("i18n").getResourceBundle().getText("EASY_FILL_PREVIOUSLY_LABEL");
104
+ var that = this;
105
+
106
+ function tryInject() {
107
+ var oGroup = that.findGroupById(oObjectPageLayout, sGroupId);
108
+ if (!oGroup || oGroup.bIsDestroyed) { return false; }
109
+
110
+ oGroup.destroyGroupElements();
103
111
 
104
112
  aFields.forEach(function(sKey) {
105
113
  var bIsEditable = Object.prototype.hasOwnProperty.call(mUpdatableLookupMap || {}, sKey);
106
114
 
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
115
  var oSmartField = new SmartField({
117
116
  value: "{" + sKey + "}",
118
117
  editable: bIsEditable,
119
118
  enabled: bIsEditable,
120
119
  contextEditable: bIsEditable,
121
- layoutData: new ColumnElementData({
122
- cellsSmall: 12,
123
- cellsLarge: 12
124
- }),
120
+ layoutData: new ColumnElementData({ cellsSmall: 12, cellsLarge: 12 }),
125
121
  editableChanged: function(oEvent) {
126
- // Prevent the SmartField from toggling its own editability;
127
- // editability is controlled exclusively by EasyFill logic
128
122
  if (oEvent.getParameter("editable") !== bIsEditable) {
129
123
  oEvent.getSource().setEditable(bIsEditable);
130
124
  }
@@ -132,45 +126,17 @@ sap.ui.define([
132
126
  });
133
127
 
134
128
  if (!bIsEditable) {
135
- // Keep read-only behavior, but render like an input field (v4-like visual parity)
136
129
  oSmartField.addStyleClass("sapUiEasyFillReadOnlyInputLike sapMInputBaseContentWrapper");
137
130
  }
138
131
 
139
- // Notify on user edits so SmartForm validity can re-gate the Confirm button.
140
132
  if (bIsEditable && typeof fnOnFieldChange === "function") {
141
133
  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
134
  oSmartField.attachInnerControlsCreated(function() {
167
135
  var oInnerControl = oSmartField.getFirstInnerControl();
168
136
  if (oInnerControl && typeof oInnerControl.getValue === "function") {
169
137
  var oBinding = oInnerControl.getBinding("value");
170
138
  var oType = oBinding && typeof oBinding.getType === "function" && oBinding.getType();
171
139
  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
140
  Promise.resolve(oBinding.setExternalValue(oInnerControl.getValue()))
175
141
  .catch(Function.prototype)
176
142
  .finally(fnOnFieldChange);
@@ -181,69 +147,67 @@ sap.ui.define([
181
147
  });
182
148
  }
183
149
 
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
- });
206
-
207
150
  if (oTransientContextForEasyFill) {
208
- // Primary field always uses transient context to show AI-proposed value.
209
- // Field editability is controlled by bIsEditable.
210
151
  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
152
  if (oTransientContextForEasyFill.getProperty(sKey) === undefined) {
216
153
  oTransientContextForEasyFill.setProperty(sKey, oLLMData[sKey]);
217
154
  }
218
155
  } else {
219
- // Fallback: without transient context, bind to original OP context.
220
156
  oSmartField.setBindingContext(oObjectPageContext);
221
157
  }
222
158
 
223
- // Previous value is always shown from the original ObjectPage context.
224
- oSmartFieldPrev.setBindingContext(oObjectPageContext);
159
+ // Read the current (pre-AI) value as a plain string and render it as
160
+ // compact secondary text directly below the SmartField.
161
+ // Mirroring V4: no second SmartField — just a plain sap.m.Text showing
162
+ // "Previously: <value>", avoiding all SmartField layout overhead.
163
+ var vCurrentValue = (oObjectPageContext && typeof oObjectPageContext.getProperty === "function")
164
+ ? oObjectPageContext.getProperty(sKey) : null;
165
+ // Use typeof check to guard against objects/arrays; Date.toLocaleDateString()
166
+ // gives a readable form for date values rather than "[object Object]".
167
+ var sCurrentValue;
168
+ if (vCurrentValue === null || vCurrentValue === undefined || vCurrentValue === "") {
169
+ sCurrentValue = "-";
170
+ } else if (vCurrentValue instanceof Date) {
171
+ sCurrentValue = vCurrentValue.toLocaleDateString();
172
+ } else if (typeof vCurrentValue === "object") {
173
+ sCurrentValue = "-";
174
+ } else {
175
+ sCurrentValue = String(vCurrentValue);
176
+ }
225
177
 
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);
178
+ var oPreviousValueText = new Text({
179
+ text: sPreviouslyLabel + " " + sCurrentValue,
180
+ wrapping: false
181
+ }).addStyleClass("sapEasyFillPreviouslyLabel");
182
+
183
+ // Two elements stacked vertically in one GroupElement:
184
+ // row 1 — primary SmartField (AI value, editable or read-only)
185
+ // row 2 — "Previously: <value>" text directly below, no gap
186
+ var oGroupElement = new GroupElement({ useHorizontalLayout: false });
187
+ oGroupElement.addElement(oSmartField);
188
+ oGroupElement.addElement(oPreviousValueText);
189
+ oGroup.addGroupElement(oGroupElement);
237
190
  });
238
191
 
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);
192
+ if (typeof fnOnFieldChange === "function") { fnOnFieldChange(); }
193
+ oLogger.debug("populateGroupFields: " + aFields.length + " fields in group '" + sGroupId + "'");
194
+ return true;
195
+ }
245
196
 
246
- return true;
197
+ // If the group control exists already (e.g. mode switch on existing sections),
198
+ // inject immediately. Otherwise the sections= binding hasn't created the group
199
+ // control yet — attach a one-shot afterRendering on the OPL to retry once
200
+ // the new sections are in the DOM.
201
+ if (!tryInject()) {
202
+ if (!oObjectPageLayout) { return; }
203
+ var oDelegate = {
204
+ onAfterRendering: function() {
205
+ oObjectPageLayout.removeEventDelegate(oDelegate);
206
+ tryInject();
207
+ }
208
+ };
209
+ oObjectPageLayout.addEventDelegate(oDelegate);
210
+ }
247
211
  },
248
212
 
249
213
  /**