@sapui5/sap.suite.ui.generic.template 1.147.2 → 1.148.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 (98) 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/controller/ControllerImplementation.js +5 -3
  4. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_bg.properties +3 -3
  5. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_da.properties +3 -3
  6. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_fr_CA.properties +2 -2
  7. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_id.properties +2 -2
  8. package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
  9. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +32 -3
  11. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
  12. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_id.properties +1 -1
  13. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_uk.properties +1 -1
  14. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  15. package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +4 -0
  16. package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +57 -25
  17. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n.properties +6 -0
  18. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en_US_saprigi.properties +4 -0
  19. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +6 -1
  20. package/src/sap/suite/ui/generic/template/ObjectPage/templateSpecificPreparationHelper.js +35 -19
  21. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
  22. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Footer.fragment.xml +103 -42
  23. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  24. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  25. package/src/sap/suite/ui/generic/template/fragments/EasyFilter.fragment.xml +3 -0
  26. package/src/sap/suite/ui/generic/template/genericUtilities/controlHelper.js +30 -29
  27. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +15 -0
  28. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  29. package/src/sap/suite/ui/generic/template/lib/CRUDHelper.js +93 -55
  30. package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +7 -0
  31. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +76 -19
  32. package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +4 -1
  33. package/src/sap/suite/ui/generic/template/lib/ComponentUtils.js +17 -7
  34. package/src/sap/suite/ui/generic/template/lib/StableIdDefinition.js +35 -34
  35. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +185 -0
  36. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
  37. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
  38. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
  39. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
  40. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +348 -422
  41. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
  42. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +142 -72
  43. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +409 -22
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +32 -8
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +14 -0
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +14 -0
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +14 -0
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +14 -0
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +14 -0
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +14 -0
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +14 -0
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +14 -0
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +14 -0
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +14 -0
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +14 -0
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +14 -0
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +14 -0
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +14 -0
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +14 -0
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +14 -0
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +15 -1
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +15 -1
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +14 -0
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +15 -1
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +14 -0
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +14 -0
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +14 -0
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +14 -0
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +20 -6
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +14 -0
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +14 -0
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +14 -0
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +14 -0
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +14 -0
  75. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +14 -0
  76. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +15 -1
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +14 -0
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +14 -0
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +14 -0
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +14 -0
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +14 -0
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +14 -0
  83. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +14 -0
  84. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +14 -0
  85. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +14 -0
  86. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +14 -0
  87. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +14 -0
  88. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +14 -0
  89. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +14 -0
  90. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +17 -3
  91. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +14 -0
  92. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +20 -6
  93. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +20 -6
  94. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
  95. package/src/sap/suite/ui/generic/template/library.js +25 -2
  96. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +22 -3
  97. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
  98. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillUpdatebleSmartForm.fragment.xml +0 -15
@@ -0,0 +1,491 @@
1
+ sap.ui.define([
2
+ "sap/ui/comp/smartfield/SmartField",
3
+ "sap/m/Text",
4
+ "sap/m/Title",
5
+ "sap/m/Table",
6
+ "sap/m/Column",
7
+ "sap/m/ColumnListItem",
8
+ "sap/m/VBox",
9
+ "sap/m/HBox",
10
+ "sap/m/SegmentedButton",
11
+ "sap/m/SegmentedButtonItem",
12
+ "sap/suite/ui/generic/template/genericUtilities/FeLogger",
13
+ "sap/suite/ui/generic/template/genericUtilities/FeError"
14
+ ], function(SmartField, Text, Title, Table, Column, ColumnListItem, VBox, HBox, SegmentedButton, SegmentedButtonItem, FeLogger, FeError) {
15
+ "use strict";
16
+
17
+ var sClassName = "lib.ai.EasyFill.EasyFillTableRenderer";
18
+ var oLogger = new FeLogger(sClassName).getLogger();
19
+ var iMaxPreviewRows = 20;
20
+
21
+ /**
22
+ * EasyFillTableRenderer
23
+ *
24
+ * Responsible for rendering the SmartTable collection preview in the EasyFill dialog.
25
+ * For each collection (navigation property) with LLM-suggested row updates, this module:
26
+ *
27
+ * 1. Creates transient OData row contexts for preview rendering.
28
+ * 2. Creates a "Previous Values" read-only table and a "New Values" editable SmartField table.
29
+ * 3. Renders a SegmentedButton toggle between the two views.
30
+ * 4. Provides reset logic to discard staged values if the user cancels.
31
+ *
32
+ * The table preview container (VBox#EasyFillTablePreviewContainer) in the dialog XML is populated
33
+ * programmatically — no table rows are declared in the fragment XML.
34
+ */
35
+ return {
36
+
37
+ /**
38
+ * Renders collection (SmartTable) previews into the EasyFillTablePreviewContainer VBox.
39
+ * For each collection with AI-suggested updates:
40
+ * - Creates transient staging contexts for the preview rows
41
+ * - Creates a Previous/New table pair with a toggle SegmentedButton
42
+ * - Appends the section to the container
43
+ *
44
+ * @param {object} mCollectionUpdates - map of { navProperty: [ rowUpdate ] } from AI response
45
+ * @param {object} mFieldMapping - full LLM field mapping (used to get item properties and descriptions)
46
+ * @param {Array} aTableValidationErrors - validation errors from value-help checks
47
+ * @param {object} oController - MVC controller (used to byId the container)
48
+ * @param {object} oRb - i18n resource bundle
49
+ * @param {object} mCollectionState - collection tracking state { mCollectionRowContexts, mCollectionRowContextsByPath, mCollectionProperties }
50
+ * @param {object} oTableStagingState - mutable table staging state { mStagedRows }
51
+ * @param {object} oObjectPageModel - the OData model used to create transient staging contexts
52
+ * @param {function} fnOnTableFieldChange - callback invoked when a preview table cell changes
53
+ */
54
+ renderCollectionPreviews: function(mCollectionUpdates, mFieldMapping, aTableValidationErrors, oController, oRb, mCollectionState, oTableStagingState, oObjectPageModel, fnOnTableFieldChange) {
55
+ var oTablePreviewContainer = oController.byId("EasyFillTablePreviewContainer");
56
+ if (!oTablePreviewContainer) {
57
+ oLogger.warning("renderCollectionPreviews: EasyFillTablePreviewContainer not found");
58
+ return;
59
+ }
60
+
61
+ // Remove all previously rendered table sections
62
+ oTablePreviewContainer.destroyItems();
63
+
64
+ this._initializeStagedCollectionRows(
65
+ mCollectionUpdates,
66
+ mFieldMapping,
67
+ mCollectionState,
68
+ oTableStagingState,
69
+ oObjectPageModel
70
+ );
71
+
72
+ var that = this;
73
+ Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
74
+ var oCollectionMeta = mFieldMapping[sCollectionName];
75
+ if (!oCollectionMeta || !oCollectionMeta.itemProperties) {
76
+ return;
77
+ }
78
+
79
+ var aPropertyNames = Object.keys(oCollectionMeta.itemProperties || {});
80
+ if (!aPropertyNames.length) {
81
+ return;
82
+ }
83
+ var aPreviewRows = (oTableStagingState.mStagedRows[sCollectionName] || []).filter(function(oStagedRow) {
84
+ return oStagedRow.isPreviewVisible;
85
+ });
86
+
87
+ if (!aPreviewRows.length) {
88
+ return;
89
+ }
90
+
91
+ // Previous values table (read-only, initially hidden)
92
+ var aPreviousRows = aPreviewRows.map(function(oPreviewRow) {
93
+ return that._buildCollectionPreviewRowFromContext(aPropertyNames, oPreviewRow.realContext);
94
+ });
95
+ var oPreviousTable = that._createPreviewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviousRows);
96
+ oPreviousTable.setVisible(false);
97
+
98
+ // New values table (editable SmartFields, initially visible)
99
+ var oNewTable = that._createEditableNewTable(
100
+ aPropertyNames,
101
+ oCollectionMeta.itemProperties,
102
+ aPreviewRows,
103
+ sCollectionName,
104
+ mCollectionState,
105
+ fnOnTableFieldChange
106
+ );
107
+
108
+ // Section title showing collection label and row count
109
+ var oSectionTitle = new Title({
110
+ level: "H5",
111
+ text: (oCollectionMeta.description || sCollectionName) + " (" + aPreviewRows.length + ")"
112
+ });
113
+
114
+ // Toggle button between "Previous" and "New" views
115
+ var oSegmentedButton = new SegmentedButton({
116
+ selectedKey: "new",
117
+ width: "auto",
118
+ items: [
119
+ new SegmentedButtonItem({ key: "previous", text: oRb.getText("EASY_FILL_TABLE_VIEW_PREVIOUS"), width: "auto" }),
120
+ new SegmentedButtonItem({ key: "new", text: oRb.getText("EASY_FILL_TABLE_VIEW_NEW"), width: "auto" })
121
+ ],
122
+ selectionChange: function() {
123
+ var sKey = oSegmentedButton.getSelectedKey();
124
+ oPreviousTable.setVisible(sKey === "previous");
125
+ oNewTable.setVisible(sKey === "new");
126
+ }
127
+ });
128
+
129
+ var oHeader = new HBox({
130
+ justifyContent: "SpaceBetween",
131
+ items: [oSectionTitle, oSegmentedButton]
132
+ }).addStyleClass("sapUiSmallMarginTop");
133
+
134
+ var oSection = new VBox({
135
+ items: [oHeader, oPreviousTable, oNewTable]
136
+ }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
137
+
138
+ oTablePreviewContainer.addItem(oSection);
139
+ oLogger.debug("renderCollectionPreviews: rendered collection '" + sCollectionName + "' with " + aPreviewRows.length + " rows");
140
+ });
141
+ },
142
+
143
+ /**
144
+ * Resets and clears all transient collection contexts created for table preview staging.
145
+ *
146
+ * @param {object} oTableStagingState - table staging state { mStagedRows }
147
+ * @param {object} oObjectPageModel - the OData model
148
+ */
149
+ resetStagedCollectionContexts: function(oTableStagingState, oObjectPageModel) {
150
+ var aTransientPaths = [];
151
+
152
+ Object.keys((oTableStagingState && oTableStagingState.mStagedRows) || {}).forEach(function(sCollectionName) {
153
+ (oTableStagingState.mStagedRows[sCollectionName] || []).forEach(function(oStagedRow) {
154
+ var oTransientContext = oStagedRow.transientContext;
155
+ var sTransientPath = oTransientContext && oTransientContext.getPath && oTransientContext.getPath();
156
+ if (sTransientPath) {
157
+ aTransientPaths.push(sTransientPath);
158
+ }
159
+ });
160
+ });
161
+
162
+ if (aTransientPaths.length && oObjectPageModel) {
163
+ oObjectPageModel.resetChanges(aTransientPaths, false, true);
164
+ }
165
+
166
+ this._clearStagedCollectionState(oTableStagingState);
167
+ oLogger.debug("resetStagedCollectionContexts: transient table contexts reset");
168
+ },
169
+
170
+ // ─── Private Helpers ─────────────────────────────────────────────────────
171
+
172
+ _initializeStagedCollectionRows: function(mCollectionUpdates, mFieldMapping, mCollectionState, oTableStagingState, oObjectPageModel) {
173
+ var that = this;
174
+ this._clearStagedCollectionState(oTableStagingState);
175
+
176
+ Object.keys(mCollectionUpdates || {}).forEach(function(sCollectionName) {
177
+ var oCollectionMeta = mFieldMapping[sCollectionName];
178
+ var aRowUpdates = mCollectionUpdates[sCollectionName] || [];
179
+ var aCollectionRowContexts = mCollectionState.mCollectionRowContexts[sCollectionName] || [];
180
+ var mCollectionPropertyMap = mCollectionState.mCollectionProperties[sCollectionName] || Object.create(null);
181
+ var aPropertyNames = Object.keys((oCollectionMeta && oCollectionMeta.itemProperties) || {});
182
+
183
+ if (!oCollectionMeta || !oCollectionMeta.entitySet || !aPropertyNames.length) {
184
+ return;
185
+ }
186
+
187
+ var mUpdatesByIdentity = Object.create(null);
188
+ aRowUpdates.forEach(function(oRowUpdate, iUpdateIndex) {
189
+ var oResolvedUpdate = that._resolveCollectionRowContext(sCollectionName, oRowUpdate, mCollectionState);
190
+ mUpdatesByIdentity[that._getCollectionRowIdentity(oResolvedUpdate, oRowUpdate, iUpdateIndex)] = oRowUpdate;
191
+ });
192
+
193
+ oTableStagingState.mStagedRows[sCollectionName] = [];
194
+
195
+ aCollectionRowContexts.forEach(function(oRealContext, iRowIndex) {
196
+ var oResolvedRow = { context: oRealContext, rowIndex: iRowIndex };
197
+ var sIdentity = that._getCollectionRowIdentity(oResolvedRow, null, iRowIndex);
198
+ var oRowUpdate = mUpdatesByIdentity[sIdentity];
199
+ var bIsPreviewVisible = iRowIndex < iMaxPreviewRows;
200
+
201
+ if (!bIsPreviewVisible && !oRowUpdate) {
202
+ return;
203
+ }
204
+
205
+ var oTransientContext = that._createTransientCollectionContext(oCollectionMeta.entitySet, oObjectPageModel);
206
+ if (!oTransientContext) {
207
+ return;
208
+ }
209
+
210
+ var oRealRowObject = oRealContext.getObject && oRealContext.getObject();
211
+ that._seedTransientContextFromRowObject(oTransientContext, oRealRowObject);
212
+ that._applyRowUpdateToTransientContext(oTransientContext, oRowUpdate, mCollectionPropertyMap, oRealRowObject);
213
+
214
+ oTableStagingState.mStagedRows[sCollectionName].push({
215
+ realContext: oRealContext,
216
+ transientContext: oTransientContext,
217
+ rowIndex: iRowIndex,
218
+ propertyNames: aPropertyNames,
219
+ isPreviewVisible: bIsPreviewVisible
220
+ });
221
+ });
222
+ });
223
+ },
224
+
225
+ /**
226
+ * Creates a read-only sap.m.Table for displaying table row data (used for "Previous Values" view).
227
+ * All cells are rendered as plain Text controls.
228
+ *
229
+ * @param {Array<string>} aPropertyNames - ordered list of property names to show as columns
230
+ * @param {object} oItemProperties - map of { propName: { description, dataType } }
231
+ * @param {Array<object>} aRows - array of plain row value objects
232
+ * @param {function} [fnCellFormatter] - optional custom cell formatter (oRow, sPropName, sRawValue) → string
233
+ * @returns {sap.m.Table}
234
+ */
235
+ _createPreviewTable: function(aPropertyNames, oItemProperties, aRows, fnCellFormatter) {
236
+ var aColumns = aPropertyNames.map(function(sPropertyName) {
237
+ return new Column({
238
+ header: new Text({
239
+ text: (oItemProperties[sPropertyName] && oItemProperties[sPropertyName].description) || sPropertyName
240
+ })
241
+ });
242
+ });
243
+
244
+ var aItems = aRows.map(function(oRow) {
245
+ return new ColumnListItem({
246
+ cells: aPropertyNames.map(function(sPropertyName) {
247
+ var sText = fnCellFormatter
248
+ ? fnCellFormatter(oRow, sPropertyName, this._stringifyCellValue(oRow[sPropertyName]))
249
+ : this._stringifyCellValue(oRow[sPropertyName]);
250
+ return new Text({ text: sText });
251
+ }.bind(this))
252
+ });
253
+ }.bind(this));
254
+
255
+ return new Table({
256
+ columns: aColumns,
257
+ items: aItems,
258
+ fixedLayout: false,
259
+ autoPopinMode: true
260
+ }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
261
+ },
262
+
263
+ /**
264
+ * Creates an editable sap.m.Table for displaying AI-suggested row updates.
265
+ * Editable cells are rendered as SmartField controls bound to the OData row context.
266
+ * Non-editable cells are rendered as plain Text controls.
267
+ *
268
+ * @param {Array<string>} aPropertyNames - ordered list of property names to show as columns
269
+ * @param {object} oItemProperties - map of { propName: { description, dataType } }
270
+ * @param {Array<object>} aPreviewRows - enriched row objects with context, newValues, etc.
271
+ * @param {string} sCollectionName - navigation property name (used to look up property metadata)
272
+ * @param {object} mCollectionState - collection tracking state (used to get property metadata)
273
+ * @returns {sap.m.Table}
274
+ */
275
+ _createEditableNewTable: function(aPropertyNames, oItemProperties, aPreviewRows, sCollectionName, mCollectionState, fnOnTableFieldChange) {
276
+ var aColumns = aPropertyNames.map(function(sPropName) {
277
+ return new Column({
278
+ header: new Text({
279
+ text: (oItemProperties[sPropName] && oItemProperties[sPropName].description) || sPropName
280
+ })
281
+ });
282
+ });
283
+
284
+ var mCollectionPropertyMap = mCollectionState.mCollectionProperties[sCollectionName] || Object.create(null);
285
+ var that = this;
286
+
287
+ var aItems = aPreviewRows.map(function(oPreviewRow) {
288
+ var oContext = oPreviewRow.transientContext;
289
+ var oRowObject = oPreviewRow.realContext && oPreviewRow.realContext.getObject && oPreviewRow.realContext.getObject();
290
+
291
+ var aCells = aPropertyNames.map(function(sPropName) {
292
+ if (!oContext) {
293
+ return new Text({ text: "" });
294
+ }
295
+
296
+ var oPropertyMeta = mCollectionPropertyMap[sPropName];
297
+ var bEditable = that._isEditableProperty(oPropertyMeta, oRowObject);
298
+
299
+ // Use a SmartField so value-help and type-specific rendering are supported
300
+ var oSmartField = new SmartField({
301
+ value: "{" + sPropName + "}",
302
+ editable: bEditable,
303
+ enabled: bEditable,
304
+ contextEditable: bEditable,
305
+ change: function() {
306
+ if (typeof fnOnTableFieldChange === "function") {
307
+ fnOnTableFieldChange();
308
+ }
309
+ },
310
+ editableChanged: function(oEvent) {
311
+ // Keep table SmartField editability deterministic during preview
312
+ if (oEvent.getParameter("editable") !== bEditable) {
313
+ oEvent.getSource().setEditable(bEditable);
314
+ }
315
+ }
316
+ });
317
+
318
+ return oSmartField;
319
+ });
320
+
321
+ var oItem = new ColumnListItem({ cells: aCells });
322
+ if (oContext) {
323
+ oItem.setBindingContext(oContext);
324
+ }
325
+ return oItem;
326
+ });
327
+
328
+ return new Table({
329
+ columns: aColumns,
330
+ items: aItems,
331
+ fixedLayout: false,
332
+ autoPopinMode: true
333
+ }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
334
+ },
335
+
336
+ /**
337
+ * Builds a plain preview row object from a given OData context.
338
+ *
339
+ * @param {Array<string>} aPropertyNames - property names to include
340
+ * @param {object} oContext - OData context providing the property values
341
+ * @returns {object} plain row data object
342
+ */
343
+ _buildCollectionPreviewRowFromContext: function(aPropertyNames, oContext) {
344
+ var oPreviewRow = {};
345
+
346
+ aPropertyNames.forEach(function(sPropertyName) {
347
+ oPreviewRow[sPropertyName] = oContext ? oContext.getProperty(sPropertyName) : "";
348
+ });
349
+
350
+ return oPreviewRow;
351
+ },
352
+
353
+ _createTransientCollectionContext: function(sEntitySetName, oObjectPageModel) {
354
+ if (!sEntitySetName || !oObjectPageModel) {
355
+ return null;
356
+ }
357
+
358
+ return oObjectPageModel.createEntry("/" + sEntitySetName, {
359
+ groupId: "EasyFillFETransientChanges",
360
+ changeSetId: "EasyFillFETransientChanges"
361
+ });
362
+ },
363
+
364
+ _seedTransientContextFromRowObject: function(oTransientContext, oRowObject) {
365
+ Object.keys(oRowObject || {}).forEach(function(sPropertyName) {
366
+ var vValue = oRowObject[sPropertyName];
367
+ if (vValue === null || vValue === undefined || typeof vValue !== "object" || vValue instanceof Date) {
368
+ oTransientContext.setProperty(sPropertyName, vValue);
369
+ }
370
+ });
371
+ },
372
+
373
+ _applyRowUpdateToTransientContext: function(oTransientContext, oRowUpdate, mCollectionPropertyMap, oRealRowObject) {
374
+ var that = this;
375
+ Object.keys(oRowUpdate || {}).forEach(function(sPropertyName) {
376
+ if (sPropertyName === "_rowIndex" || sPropertyName === "_contextPath") {
377
+ return;
378
+ }
379
+
380
+ var oPropertyMeta = mCollectionPropertyMap[sPropertyName];
381
+ if (!that._isEditableProperty(oPropertyMeta, oRealRowObject)) {
382
+ return;
383
+ }
384
+
385
+ oTransientContext.setProperty(sPropertyName, oRowUpdate[sPropertyName]);
386
+ });
387
+ },
388
+
389
+ _clearStagedCollectionState: function(oTableStagingState) {
390
+ Object.keys((oTableStagingState && oTableStagingState.mStagedRows) || {}).forEach(function(sCollectionName) {
391
+ delete oTableStagingState.mStagedRows[sCollectionName];
392
+ });
393
+ },
394
+
395
+ /**
396
+ * Resolves an OData row context for a given collection row update.
397
+ * Prefers context path lookup; falls back to row index.
398
+ *
399
+ * @param {string} sCollectionName - navigation property name
400
+ * @param {object} oRowUpdate - the AI row update object ({ _contextPath?, _rowIndex? })
401
+ * @param {object} mCollectionState - collection tracking state
402
+ * @returns {{ context, rowIndex }|null}
403
+ */
404
+ _resolveCollectionRowContext: function(sCollectionName, oRowUpdate, mCollectionState) {
405
+ var mByPath = mCollectionState.mCollectionRowContextsByPath[sCollectionName] || Object.create(null);
406
+ var sContextPath = oRowUpdate && oRowUpdate._contextPath;
407
+
408
+ // Prefer context path (more stable across refreshes)
409
+ if (typeof sContextPath === "string" && mByPath[sContextPath]) {
410
+ return mByPath[sContextPath];
411
+ }
412
+
413
+ // Fallback to row index
414
+ var iRowIndex = oRowUpdate && oRowUpdate._rowIndex;
415
+ var aRowContexts = mCollectionState.mCollectionRowContexts[sCollectionName] || [];
416
+ if (typeof iRowIndex === "number" && aRowContexts[iRowIndex]) {
417
+ return { context: aRowContexts[iRowIndex], rowIndex: iRowIndex };
418
+ }
419
+
420
+ return null;
421
+ },
422
+
423
+ /**
424
+ * Returns a stable string identity for a collection row, used as a map key.
425
+ * Priority: context path → _contextPath from update → _rowIndex → fallback index string.
426
+ *
427
+ * @param {object} oResolvedRow - { context, rowIndex } or null
428
+ * @param {object} oRowUpdate - the AI row update object
429
+ * @param {number} iFallbackIndex - fallback numeric index
430
+ * @returns {string}
431
+ */
432
+ _getCollectionRowIdentity: function(oResolvedRow, oRowUpdate, iFallbackIndex) {
433
+ if (oResolvedRow && oResolvedRow.context && oResolvedRow.context.getPath) {
434
+ return oResolvedRow.context.getPath();
435
+ }
436
+ if (oRowUpdate && typeof oRowUpdate._contextPath === "string") {
437
+ return oRowUpdate._contextPath;
438
+ }
439
+ if (oRowUpdate && typeof oRowUpdate._rowIndex === "number") {
440
+ return "index:" + String(oRowUpdate._rowIndex);
441
+ }
442
+ return "row:" + String(iFallbackIndex);
443
+ },
444
+
445
+ /**
446
+ * Converts a raw cell value to a display string for read-only table cells.
447
+ * Handles null/undefined, Date objects, and primitives.
448
+ *
449
+ * @param {*} vValue
450
+ * @returns {string}
451
+ */
452
+ _stringifyCellValue: function(vValue) {
453
+ if (vValue === null || vValue === undefined) {
454
+ return "";
455
+ }
456
+ if (vValue instanceof Date) {
457
+ return vValue.toISOString();
458
+ }
459
+ return String(vValue);
460
+ },
461
+
462
+ /**
463
+ * Checks whether a collection property is editable given the current row data object.
464
+ * Delegates to the same annotation checks used in EasyFillFieldCollector.isEditableByContextObject.
465
+ * Duplicated here to keep TableRenderer self-contained (no circular dependency).
466
+ *
467
+ * @param {object} oPropertyMeta - OData property metadata
468
+ * @param {object} oRowObject - plain row data object
469
+ * @returns {boolean}
470
+ */
471
+ _isEditableProperty: function(oPropertyMeta, oRowObject) {
472
+ if (!oPropertyMeta) {
473
+ return false;
474
+ }
475
+ var sUpdatable = oPropertyMeta["sap:updatable"];
476
+ if (sUpdatable) {
477
+ return sUpdatable !== "false";
478
+ }
479
+ var oAnnotation = oPropertyMeta["com.sap.vocabularies.Common.v1.FieldControl"];
480
+ if (oAnnotation && oAnnotation["EnumMember"]) {
481
+ return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly";
482
+ }
483
+ var sFieldControl = oPropertyMeta["sap:field-control"];
484
+ if (sFieldControl) {
485
+ var iFieldControlValue = oRowObject && oRowObject[sFieldControl];
486
+ return iFieldControlValue !== 1;
487
+ }
488
+ return true;
489
+ }
490
+ };
491
+ });