create-ui5-freestyle-lr 0.2.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.
- package/README.md +141 -0
- package/package.json +34 -0
- package/src/cli.js +74 -0
- package/src/metadata/fetch.js +42 -0
- package/src/metadata/parse.js +104 -0
- package/src/prompts/basic.js +43 -0
- package/src/prompts/fields.js +144 -0
- package/src/prompts/odata.js +57 -0
- package/src/render/buildContext.js +67 -0
- package/src/render/controlForType.js +136 -0
- package/src/render/engine.js +81 -0
- package/src/templates/README.md.ejs +62 -0
- package/src/templates/_valueHelp.fragment.xml.ejs +17 -0
- package/src/templates/package.json.ejs +14 -0
- package/src/templates/ui5.yaml.ejs +28 -0
- package/src/templates/webapp/Component.js.ejs +47 -0
- package/src/templates/webapp/controller/App.controller.js +29 -0
- package/src/templates/webapp/controller/BaseController.js +35 -0
- package/src/templates/webapp/controller/ErrorHandler.js +71 -0
- package/src/templates/webapp/controller/NotFound.controller.js +12 -0
- package/src/templates/webapp/controller/Worklist.controller.js.ejs +1158 -0
- package/src/templates/webapp/css/style.css +17 -0
- package/src/templates/webapp/i18n/i18n.properties.ejs +83 -0
- package/src/templates/webapp/i18n/i18n_de.properties.ejs +83 -0
- package/src/templates/webapp/index.html.ejs +52 -0
- package/src/templates/webapp/localService/backendCheck.js.ejs +52 -0
- package/src/templates/webapp/localService/mockserver.js.ejs +29 -0
- package/src/templates/webapp/manifest.json.ejs +106 -0
- package/src/templates/webapp/model/formatter.js +148 -0
- package/src/templates/webapp/model/models.js +23 -0
- package/src/templates/webapp/view/App.view.xml +10 -0
- package/src/templates/webapp/view/NotFound.view.xml +12 -0
- package/src/templates/webapp/view/Worklist.view.xml.ejs +173 -0
- package/src/templates/webapp/view/fragments/GroupDialog.fragment.xml.ejs +11 -0
- package/src/templates/webapp/view/fragments/InfoPopover.fragment.xml +38 -0
- package/src/templates/webapp/view/fragments/SortDialog.fragment.xml.ejs +11 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
html, body, body > .sapUiBody {
|
|
2
|
+
height: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.infoBarText {
|
|
6
|
+
font-style: italic;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.lrTable,
|
|
10
|
+
.lrTable .sapMListTbl,
|
|
11
|
+
.lrTable .sapMListTblCnt,
|
|
12
|
+
.lrTable .sapMListHdr,
|
|
13
|
+
.lrTable .sapMListHdrTBar,
|
|
14
|
+
.lrTable .sapMIBar,
|
|
15
|
+
.lrTable .sapMOTB {
|
|
16
|
+
border-radius: 0 !important;
|
|
17
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# App
|
|
2
|
+
appTitle=<%= appTitle %>
|
|
3
|
+
appDescription=<%= appDescription %>
|
|
4
|
+
errorText=An unexpected error occurred.
|
|
5
|
+
|
|
6
|
+
# Worklist
|
|
7
|
+
worklistTitle=<%= appTitle %>
|
|
8
|
+
worklistViewTitle=<%= appTitle %>
|
|
9
|
+
tableTitleCount=Items ({0}/{1})
|
|
10
|
+
tableTitleNoCount=Items
|
|
11
|
+
noDataText=No items to display
|
|
12
|
+
|
|
13
|
+
# Search
|
|
14
|
+
searchField.placeholder=Search
|
|
15
|
+
|
|
16
|
+
# Columns
|
|
17
|
+
<% columns.forEach(function (col) { -%>
|
|
18
|
+
col.<%= col.name %>=<%= col.label %>
|
|
19
|
+
<% }) -%>
|
|
20
|
+
col.info=Info
|
|
21
|
+
|
|
22
|
+
# Filters
|
|
23
|
+
<% filters.forEach(function (f) { -%>
|
|
24
|
+
filter.<%= f.fieldName %>=<%= f.label %>
|
|
25
|
+
<% }) -%>
|
|
26
|
+
|
|
27
|
+
# Pagination
|
|
28
|
+
rowsPerPage=Rows per page
|
|
29
|
+
rowsPerPageError=Rows per page must be between {0} and {1}.
|
|
30
|
+
|
|
31
|
+
# Info bar
|
|
32
|
+
info.filteredBy=Filtered by {0}
|
|
33
|
+
info.sortedBy=Sorted by {0} ({1})
|
|
34
|
+
info.groupedBy=Grouped by {0}
|
|
35
|
+
info.searchedBy=Searched: "{0}"
|
|
36
|
+
sort.asc=Ascending
|
|
37
|
+
sort.desc=Descending
|
|
38
|
+
group.empty=(empty)
|
|
39
|
+
|
|
40
|
+
# Toolbar
|
|
41
|
+
columnSettings=Columns
|
|
42
|
+
tooltip.sort=Sort
|
|
43
|
+
tooltip.group=Group
|
|
44
|
+
tooltip.refresh=Refresh
|
|
45
|
+
tooltip.export=Export to Excel
|
|
46
|
+
tooltip.autoRefresh=Auto-refresh every 30s
|
|
47
|
+
autoRefresh.btnOff=Auto: Off
|
|
48
|
+
autoRefresh.btnOn=Auto: On
|
|
49
|
+
autoRefresh.on=Auto-refresh enabled (every 30s)
|
|
50
|
+
autoRefresh.off=Auto-refresh disabled
|
|
51
|
+
tableRefreshed=Table refreshed
|
|
52
|
+
lastRefreshed.label=Last updated
|
|
53
|
+
variant.saved=View saved
|
|
54
|
+
close=Close
|
|
55
|
+
|
|
56
|
+
# Export
|
|
57
|
+
export.noData=Nothing to export
|
|
58
|
+
export.success=Export complete
|
|
59
|
+
export.error=Export failed
|
|
60
|
+
export.largeConfirm=You are about to export {0} rows. Continue?
|
|
61
|
+
export.dialogTitle=Exporting…
|
|
62
|
+
export.dialogText=Generating Excel file
|
|
63
|
+
|
|
64
|
+
# Multi-select / bulk action
|
|
65
|
+
selectedCount=selected
|
|
66
|
+
bulkAction=Action
|
|
67
|
+
bulkActionDemo={0} item(s) actioned
|
|
68
|
+
clearSelection=Clear selection
|
|
69
|
+
|
|
70
|
+
# Empty state
|
|
71
|
+
noDataIllustrationTitle=No items
|
|
72
|
+
noDataIllustrationDesc=Try adjusting your filters or refresh the table.
|
|
73
|
+
|
|
74
|
+
# Info popover (demo — see Worklist.controller.js onInfoPopoverPress)
|
|
75
|
+
link.info=Info
|
|
76
|
+
infoPopoverTitle=Row Details
|
|
77
|
+
infoPopoverKey=Key
|
|
78
|
+
infoPopoverDetail=Detail
|
|
79
|
+
infoPopoverTimestamp=Timestamp
|
|
80
|
+
|
|
81
|
+
# Not Found
|
|
82
|
+
notFoundTitle=Not Found
|
|
83
|
+
notFoundText=The requested resource was not found.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# App
|
|
2
|
+
appTitle=<%= appTitle %>
|
|
3
|
+
appDescription=<%= appDescription %>
|
|
4
|
+
errorText=Ein unerwarteter Fehler ist aufgetreten.
|
|
5
|
+
|
|
6
|
+
# Worklist
|
|
7
|
+
worklistTitle=<%= appTitle %>
|
|
8
|
+
worklistViewTitle=<%= appTitle %>
|
|
9
|
+
tableTitleCount=Einträge ({0}/{1})
|
|
10
|
+
tableTitleNoCount=Einträge
|
|
11
|
+
noDataText=Keine Einträge vorhanden
|
|
12
|
+
|
|
13
|
+
# Search
|
|
14
|
+
searchField.placeholder=Suchen
|
|
15
|
+
|
|
16
|
+
# Columns — translate field labels manually
|
|
17
|
+
<% columns.forEach(function (col) { -%>
|
|
18
|
+
col.<%= col.name %>=<%= col.label %>
|
|
19
|
+
<% }) -%>
|
|
20
|
+
col.info=Info
|
|
21
|
+
|
|
22
|
+
# Filters — translate filter labels manually
|
|
23
|
+
<% filters.forEach(function (f) { -%>
|
|
24
|
+
filter.<%= f.fieldName %>=<%= f.label %>
|
|
25
|
+
<% }) -%>
|
|
26
|
+
|
|
27
|
+
# Pagination
|
|
28
|
+
rowsPerPage=Zeilen pro Seite
|
|
29
|
+
rowsPerPageError=Zeilen pro Seite muss zwischen {0} und {1} liegen.
|
|
30
|
+
|
|
31
|
+
# Info bar
|
|
32
|
+
info.filteredBy=Gefiltert nach {0}
|
|
33
|
+
info.sortedBy=Sortiert nach {0} ({1})
|
|
34
|
+
info.groupedBy=Gruppiert nach {0}
|
|
35
|
+
info.searchedBy=Gesucht: "{0}"
|
|
36
|
+
sort.asc=Aufsteigend
|
|
37
|
+
sort.desc=Absteigend
|
|
38
|
+
group.empty=(leer)
|
|
39
|
+
|
|
40
|
+
# Toolbar
|
|
41
|
+
columnSettings=Spalten
|
|
42
|
+
tooltip.sort=Sortieren
|
|
43
|
+
tooltip.group=Gruppieren
|
|
44
|
+
tooltip.refresh=Aktualisieren
|
|
45
|
+
tooltip.export=Nach Excel exportieren
|
|
46
|
+
tooltip.autoRefresh=Auto-Aktualisierung alle 30s
|
|
47
|
+
autoRefresh.btnOff=Auto: Aus
|
|
48
|
+
autoRefresh.btnOn=Auto: An
|
|
49
|
+
autoRefresh.on=Auto-Aktualisierung aktiv (alle 30s)
|
|
50
|
+
autoRefresh.off=Auto-Aktualisierung deaktiviert
|
|
51
|
+
tableRefreshed=Tabelle aktualisiert
|
|
52
|
+
lastRefreshed.label=Zuletzt aktualisiert
|
|
53
|
+
variant.saved=Ansicht gespeichert
|
|
54
|
+
close=Schließen
|
|
55
|
+
|
|
56
|
+
# Export
|
|
57
|
+
export.noData=Keine Daten zum Exportieren
|
|
58
|
+
export.success=Export abgeschlossen
|
|
59
|
+
export.error=Export fehlgeschlagen
|
|
60
|
+
export.largeConfirm=Sie exportieren {0} Zeilen. Fortfahren?
|
|
61
|
+
export.dialogTitle=Export läuft…
|
|
62
|
+
export.dialogText=Excel-Datei wird erstellt
|
|
63
|
+
|
|
64
|
+
# Multi-select / bulk action
|
|
65
|
+
selectedCount=ausgewählt
|
|
66
|
+
bulkAction=Aktion
|
|
67
|
+
bulkActionDemo={0} Eintrag/Einträge bearbeitet
|
|
68
|
+
clearSelection=Auswahl aufheben
|
|
69
|
+
|
|
70
|
+
# Empty state
|
|
71
|
+
noDataIllustrationTitle=Keine Einträge
|
|
72
|
+
noDataIllustrationDesc=Filter anpassen oder Tabelle aktualisieren.
|
|
73
|
+
|
|
74
|
+
# Info popover
|
|
75
|
+
link.info=Info
|
|
76
|
+
infoPopoverTitle=Zeilendetails
|
|
77
|
+
infoPopoverKey=Schlüssel
|
|
78
|
+
infoPopoverDetail=Detail
|
|
79
|
+
infoPopoverTimestamp=Zeitstempel
|
|
80
|
+
|
|
81
|
+
# Not Found
|
|
82
|
+
notFoundTitle=Nicht gefunden
|
|
83
|
+
notFoundText=Die angeforderte Ressource wurde nicht gefunden.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title><%= appTitle %></title>
|
|
8
|
+
<!--
|
|
9
|
+
flexibilityServices wires sap.ui.fl (used by VariantManagement) to a
|
|
10
|
+
connector. Default = LocalStorageConnector, so saved variants live in
|
|
11
|
+
the browser. To persist on a SAP backend, swap to "LrepConnector"
|
|
12
|
+
(requires /sap/bc/lrep to be reachable). Multiple connectors can be
|
|
13
|
+
chained; see sap.ui.fl docs.
|
|
14
|
+
-->
|
|
15
|
+
<script
|
|
16
|
+
id="sap-ui-bootstrap"
|
|
17
|
+
src="resources/sap-ui-core.js"
|
|
18
|
+
data-sap-ui-theme="sap_horizon"
|
|
19
|
+
data-sap-ui-resourceroots='{"<%= appId %>": "./"}'
|
|
20
|
+
data-sap-ui-compat-version="edge"
|
|
21
|
+
data-sap-ui-async="true"
|
|
22
|
+
data-sap-ui-xx-waitForTheme="true"
|
|
23
|
+
data-sap-ui-flexibilityServices='[{"connector":"LocalStorageConnector"}]'>
|
|
24
|
+
</script>
|
|
25
|
+
<script>
|
|
26
|
+
// Wait for UI5 core, run the backend preflight check, then mount the
|
|
27
|
+
// Component. If the backend is reachable the app uses real data; if
|
|
28
|
+
// not (e.g. offline dev, no proxy), backendCheck starts a MockServer
|
|
29
|
+
// that fakes data from localService/metadata.xml.
|
|
30
|
+
sap.ui.require(["sap/ui/core/Core"], function (Core) {
|
|
31
|
+
Core.ready().then(function () {
|
|
32
|
+
sap.ui.require([
|
|
33
|
+
"sap/ui/core/ComponentContainer",
|
|
34
|
+
"<%= namespacePath %>/localService/backendCheck"
|
|
35
|
+
], function (ComponentContainer, backendCheck) {
|
|
36
|
+
backendCheck.decide().then(function () {
|
|
37
|
+
new ComponentContainer({
|
|
38
|
+
id: "container",
|
|
39
|
+
name: "<%= appId %>",
|
|
40
|
+
settings: { id: "<%= appName %>" },
|
|
41
|
+
height: "100%",
|
|
42
|
+
async: true
|
|
43
|
+
}).placeAt("content");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
49
|
+
</head>
|
|
50
|
+
<body class="sapUiBody" id="content">
|
|
51
|
+
</body>
|
|
52
|
+
</html>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
sap.ui.define([
|
|
2
|
+
"./mockserver"
|
|
3
|
+
], function (mockserver) {
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const SERVICE_URL = "<%= serviceUrl %>";
|
|
7
|
+
const TIMEOUT_MS = 3000;
|
|
8
|
+
|
|
9
|
+
function ping() {
|
|
10
|
+
return new Promise(function (resolve) {
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const timer = setTimeout(function () {
|
|
13
|
+
controller.abort();
|
|
14
|
+
resolve(false);
|
|
15
|
+
}, TIMEOUT_MS);
|
|
16
|
+
|
|
17
|
+
fetch(SERVICE_URL + "$metadata", {
|
|
18
|
+
method: "HEAD",
|
|
19
|
+
signal: controller.signal,
|
|
20
|
+
credentials: "include"
|
|
21
|
+
}).then(function (r) {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
// 2xx = backend reachable and accessible.
|
|
24
|
+
// 401 / 403 = backend alive, auth issue only — still treat as "real".
|
|
25
|
+
resolve(r.ok || r.status === 401 || r.status === 403);
|
|
26
|
+
}).catch(function () {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
resolve(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
/**
|
|
35
|
+
* Pings the configured OData service. If reachable, returns without
|
|
36
|
+
* side-effects. If unreachable, starts a MockServer so the app works
|
|
37
|
+
* offline with synthetic data generated from the local metadata.xml.
|
|
38
|
+
*/
|
|
39
|
+
decide: function () {
|
|
40
|
+
return ping().then(function (isReachable) {
|
|
41
|
+
if (!isReachable) {
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.info("[backendCheck] Backend not reachable — starting MockServer");
|
|
44
|
+
mockserver.init();
|
|
45
|
+
} else {
|
|
46
|
+
// eslint-disable-next-line no-console
|
|
47
|
+
console.info("[backendCheck] Backend is reachable — using real data");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
sap.ui.define([
|
|
2
|
+
"sap/ui/core/util/MockServer"
|
|
3
|
+
], function (MockServer) {
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
/**
|
|
8
|
+
* Starts a MockServer that reads localService/metadata.xml and
|
|
9
|
+
* returns randomly generated rows matching the schema for every
|
|
10
|
+
* OData request going to the main service URL.
|
|
11
|
+
*/
|
|
12
|
+
init: function () {
|
|
13
|
+
const oMockServer = new MockServer({
|
|
14
|
+
rootUri: "<%= serviceUrl %>"
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
MockServer.config({
|
|
18
|
+
autoRespond: true,
|
|
19
|
+
autoRespondAfter: 300
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
oMockServer.simulate(
|
|
23
|
+
sap.ui.require.toUrl("<%= namespacePath %>/localService/metadata.xml")
|
|
24
|
+
);
|
|
25
|
+
oMockServer.start();
|
|
26
|
+
return oMockServer;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_version": "1.60.0",
|
|
3
|
+
"sap.app": {
|
|
4
|
+
"id": "<%= appId %>",
|
|
5
|
+
"type": "application",
|
|
6
|
+
"i18n": "i18n/i18n.properties",
|
|
7
|
+
"title": "{{appTitle}}",
|
|
8
|
+
"description": "{{appDescription}}",
|
|
9
|
+
"applicationVersion": {
|
|
10
|
+
"version": "1.0.0"
|
|
11
|
+
},
|
|
12
|
+
"dataSources": {
|
|
13
|
+
"mainService": {
|
|
14
|
+
"uri": "<%= serviceUrl %>",
|
|
15
|
+
"type": "OData",
|
|
16
|
+
"settings": {
|
|
17
|
+
"odataVersion": "2.0",
|
|
18
|
+
"localUri": "localService/metadata.xml"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"sap.ui": {
|
|
24
|
+
"technology": "UI5",
|
|
25
|
+
"fullWidth": true,
|
|
26
|
+
"icons": {
|
|
27
|
+
"icon": "sap-icon://task"
|
|
28
|
+
},
|
|
29
|
+
"deviceTypes": {
|
|
30
|
+
"desktop": true,
|
|
31
|
+
"tablet": true,
|
|
32
|
+
"phone": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"sap.ui5": {
|
|
36
|
+
"flexEnabled": true,
|
|
37
|
+
"rootView": {
|
|
38
|
+
"viewName": "<%= appId %>.view.App",
|
|
39
|
+
"type": "XML",
|
|
40
|
+
"async": true,
|
|
41
|
+
"id": "app"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"minUI5Version": "<%= minUI5Version %>",
|
|
45
|
+
"libs": {
|
|
46
|
+
"sap.ui.core": {},
|
|
47
|
+
"sap.m": {},
|
|
48
|
+
"sap.f": {},
|
|
49
|
+
"sap.ui.comp": {},
|
|
50
|
+
"sap.ui.layout": {},
|
|
51
|
+
"sap.uxap": {},
|
|
52
|
+
"sap.ui.export": {},
|
|
53
|
+
"sap.ui.fl": {}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"contentDensities": {
|
|
57
|
+
"compact": true,
|
|
58
|
+
"cozy": true
|
|
59
|
+
},
|
|
60
|
+
"resources": {
|
|
61
|
+
"css": [
|
|
62
|
+
{ "uri": "css/style.css" }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"models": {
|
|
66
|
+
"i18n": {
|
|
67
|
+
"type": "sap.ui.model.resource.ResourceModel",
|
|
68
|
+
"settings": {
|
|
69
|
+
"bundleName": "<%= appId %>.i18n.i18n",
|
|
70
|
+
"supportedLocales": [""<% if (hasGerman) { %>, "de"<% } %>],
|
|
71
|
+
"fallbackLocale": ""
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"": {
|
|
75
|
+
"dataSource": "mainService",
|
|
76
|
+
"preload": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"routing": {
|
|
80
|
+
"config": {
|
|
81
|
+
"routerClass": "sap.m.routing.Router",
|
|
82
|
+
"viewType": "XML",
|
|
83
|
+
"viewPath": "<%= appId %>.view",
|
|
84
|
+
"controlId": "app",
|
|
85
|
+
"controlAggregation": "pages",
|
|
86
|
+
"bypassed": { "target": ["notFound"] },
|
|
87
|
+
"async": true
|
|
88
|
+
},
|
|
89
|
+
"routes": [
|
|
90
|
+
{ "pattern": ":?query:", "name": "worklist", "target": ["worklist"] }
|
|
91
|
+
],
|
|
92
|
+
"targets": {
|
|
93
|
+
"worklist": {
|
|
94
|
+
"viewName": "Worklist",
|
|
95
|
+
"viewId": "worklist",
|
|
96
|
+
"viewLevel": 1,
|
|
97
|
+
"title": "{i18n>worklistViewTitle}"
|
|
98
|
+
},
|
|
99
|
+
"notFound": {
|
|
100
|
+
"viewName": "NotFound",
|
|
101
|
+
"viewId": "notFound"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
sap.ui.define([
|
|
2
|
+
"sap/ui/core/format/DateFormat"
|
|
3
|
+
], function (DateFormat) {
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Date+time formatter. Default pattern "dd.MM.yyyy HH:mm:ss".
|
|
10
|
+
* Pass a custom pattern as 2nd arg for variations.
|
|
11
|
+
*/
|
|
12
|
+
formatDate: function (vValue, sPattern) {
|
|
13
|
+
if (!vValue) return "";
|
|
14
|
+
const oDate = vValue instanceof Date ? vValue : new Date(vValue);
|
|
15
|
+
if (isNaN(oDate.getTime())) return "";
|
|
16
|
+
return DateFormat.getDateTimeInstance({
|
|
17
|
+
pattern: sPattern || "dd.MM.yyyy HH:mm:ss"
|
|
18
|
+
}).format(oDate);
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Date-only formatter "dd.MM.yyyy". Useful as ObjectIdentifier title.
|
|
23
|
+
*/
|
|
24
|
+
formatDateOnly: function (vValue) {
|
|
25
|
+
if (!vValue) return "";
|
|
26
|
+
const oDate = vValue instanceof Date ? vValue : new Date(vValue);
|
|
27
|
+
if (isNaN(oDate.getTime())) return "";
|
|
28
|
+
return DateFormat.getDateInstance({ pattern: "dd.MM.yyyy" }).format(oDate);
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Time-only formatter "HH:mm:ss". Useful as ObjectIdentifier secondary text.
|
|
33
|
+
*/
|
|
34
|
+
formatTimeOnly: function (vValue) {
|
|
35
|
+
if (!vValue) return "";
|
|
36
|
+
const oDate = vValue instanceof Date ? vValue : new Date(vValue);
|
|
37
|
+
if (isNaN(oDate.getTime())) return "";
|
|
38
|
+
return DateFormat.getTimeInstance({ pattern: "HH:mm:ss" }).format(oDate);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maps a status value to one of: "None" / "Success" / "Warning" / "Error" / "Information".
|
|
43
|
+
* Used by GenericTag (status attribute) and ColumnListItem (highlight attribute).
|
|
44
|
+
*
|
|
45
|
+
* The defaults below cover common English status vocabulary. To support
|
|
46
|
+
* your own domain-specific codes (e.g. "MTCH", "01", "OPEN"), add cases
|
|
47
|
+
* at the TOP of the function — they take priority over the generic ones.
|
|
48
|
+
*/
|
|
49
|
+
formatStatusState: function (sStatus) {
|
|
50
|
+
if (sStatus === undefined || sStatus === null || sStatus === "") return "None";
|
|
51
|
+
const s = String(sStatus).toUpperCase();
|
|
52
|
+
|
|
53
|
+
// ---- Add your domain-specific status codes here ----
|
|
54
|
+
// if (s === "MTCH") return "Success";
|
|
55
|
+
// if (s === "NMTC") return "Error";
|
|
56
|
+
// if (s === "CMTC") return "Warning";
|
|
57
|
+
// ----------------------------------------------------
|
|
58
|
+
|
|
59
|
+
// Generic English status terms — cover most cases out of the box
|
|
60
|
+
if (/^(OK|SUCCESS|SUCCESSFUL|SUCC|DONE|COMPLETE|COMPLETED|ACTIVE|APPROVED|PASSED|YES|TRUE|1)$/.test(s)) return "Success";
|
|
61
|
+
if (/^(ERR|ERROR|FAIL|FAILED|REJECTED|INACTIVE|CANCELLED|CANCELED|BLOCKED|NO|FALSE|0)$/.test(s)) return "Error";
|
|
62
|
+
if (/^(WRN|WARN|WARNING|PENDING|PEND|TODO|DRAFT|HOLD|REVIEW|IN_PROGRESS)$/.test(s)) return "Warning";
|
|
63
|
+
if (/^(INFO|INFORMATION|NEW|OPEN|UNREAD)$/.test(s)) return "Information";
|
|
64
|
+
|
|
65
|
+
return "None";
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* DEMO ONLY — always returns "Success".
|
|
70
|
+
*
|
|
71
|
+
* Bind ColumnListItem `highlight` (or GenericTag `status`) to this
|
|
72
|
+
* formatter to verify the highlight is visually wired up — every row
|
|
73
|
+
* gets a green bar regardless of data. Useful when MockServer returns
|
|
74
|
+
* random strings that don't match any real status code.
|
|
75
|
+
*
|
|
76
|
+
* Switch back to `formatStatusState` once your real backend (or your
|
|
77
|
+
* domain mapping inside formatStatusState) is in place.
|
|
78
|
+
*/
|
|
79
|
+
formatStatusStateDemo: function () {
|
|
80
|
+
return "Success";
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Strip leading zeros (e.g. SAP partner ID "0000123456" -> "123456").
|
|
85
|
+
*/
|
|
86
|
+
formatPartnerId: function (sId) {
|
|
87
|
+
if (!sId) return "";
|
|
88
|
+
return String(sId).replace(/^0+/, "");
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wrap value in <strong><code> — bold monospace, most visually prominent.
|
|
93
|
+
* Use for primary identifiers like GUIDs.
|
|
94
|
+
*/
|
|
95
|
+
formatStrongCode: function (sValue) {
|
|
96
|
+
if (sValue === undefined || sValue === null) return "";
|
|
97
|
+
return "<strong><code>" + sValue + "</code></strong>";
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Wrap value in <code> — monospace, lighter weight.
|
|
102
|
+
* Use for secondary identifiers like CustomerId, PartnerId.
|
|
103
|
+
*/
|
|
104
|
+
formatCode: function (sValue) {
|
|
105
|
+
if (sValue === undefined || sValue === null) return "";
|
|
106
|
+
return "<code>" + sValue + "</code>";
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Wrap value in <cite> — italic, reference-like.
|
|
111
|
+
* Use for keys, UUIDs, references.
|
|
112
|
+
*/
|
|
113
|
+
formatCite: function (sValue) {
|
|
114
|
+
if (sValue === undefined || sValue === null) return "";
|
|
115
|
+
return "<cite>" + sValue + "</cite>";
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Round a numeric value to 2 decimals.
|
|
120
|
+
*/
|
|
121
|
+
numberUnit: function (sValue) {
|
|
122
|
+
if (sValue === undefined || sValue === null || sValue === "") return "";
|
|
123
|
+
const n = parseFloat(sValue);
|
|
124
|
+
return isNaN(n) ? "" : n.toFixed(2);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Animate a numeric JSON-model value from current to fTarget over
|
|
129
|
+
* iDuration ms (ease-out-cubic). Useful for progress bars.
|
|
130
|
+
*/
|
|
131
|
+
_animateValue: function (oModel, sPath, fTarget, iDuration) {
|
|
132
|
+
oModel._animations = oModel._animations || {};
|
|
133
|
+
if (oModel._animations[sPath]) oModel._animations[sPath].cancelled = true;
|
|
134
|
+
const oToken = { cancelled: false };
|
|
135
|
+
oModel._animations[sPath] = oToken;
|
|
136
|
+
const fStart = oModel.getProperty(sPath) || 0;
|
|
137
|
+
const iStartTime = performance.now();
|
|
138
|
+
const ease = (t) => 1 - Math.pow(1 - t, 3);
|
|
139
|
+
const step = (iNow) => {
|
|
140
|
+
if (oToken.cancelled) return;
|
|
141
|
+
const iProgress = Math.min((iNow - iStartTime) / iDuration, 1);
|
|
142
|
+
oModel.setProperty(sPath, Math.round(fStart + (fTarget - fStart) * ease(iProgress)));
|
|
143
|
+
if (iProgress < 1) requestAnimationFrame(step);
|
|
144
|
+
};
|
|
145
|
+
requestAnimationFrame(step);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
sap.ui.define([
|
|
2
|
+
"sap/ui/model/json/JSONModel",
|
|
3
|
+
"sap/ui/Device",
|
|
4
|
+
"sap/base/util/ObjectPath"
|
|
5
|
+
], function (JSONModel, Device, ObjectPath) {
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
createDeviceModel: function () {
|
|
10
|
+
const oModel = new JSONModel(Device);
|
|
11
|
+
oModel.setDefaultBindingMode("OneWay");
|
|
12
|
+
return oModel;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
createFLPModel: function () {
|
|
16
|
+
const fnGetUser = ObjectPath.get("sap.ushell.Container.getUser");
|
|
17
|
+
const bIsShareInJamActive = fnGetUser ? fnGetUser().isJamActive() : false;
|
|
18
|
+
const oModel = new JSONModel({ isShareInJamActive: bIsShareInJamActive });
|
|
19
|
+
oModel.setDefaultBindingMode("OneWay");
|
|
20
|
+
return oModel;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<mvc:View
|
|
2
|
+
controllerName="__APP_ID__.controller.NotFound"
|
|
3
|
+
xmlns="sap.m"
|
|
4
|
+
xmlns:mvc="sap.ui.core.mvc">
|
|
5
|
+
<MessagePage
|
|
6
|
+
title="{i18n>notFoundTitle}"
|
|
7
|
+
text="{i18n>notFoundText}"
|
|
8
|
+
description=""
|
|
9
|
+
icon="sap-icon://document"
|
|
10
|
+
showNavButton="true"
|
|
11
|
+
navButtonPress=".onNavBack"/>
|
|
12
|
+
</mvc:View>
|