diff --git a/app/manager/.babelrc.json b/app/manager/.babelrc.json new file mode 100644 index 00000000..eb5a0c0f --- /dev/null +++ b/app/manager/.babelrc.json @@ -0,0 +1,22 @@ +{ + "presets": [ + "@babel/preset-env", + [ + "transform-ui5", + { + "overridesToOverride": true + } + ], + "@babel/preset-typescript" + ], + "plugins": [ + "transform-remove-console", + [ + "transform-async-to-promises", + { + "inlineHelpers": true + } + ] + ], + "sourceMaps": true +} \ No newline at end of file diff --git a/app/manager/.eslintrc b/app/manager/.eslintrc new file mode 100644 index 00000000..8b435157 --- /dev/null +++ b/app/manager/.eslintrc @@ -0,0 +1,4 @@ +{ + "extends": "plugin:@sap-ux/eslint-plugin-fiori-tools/defaultTS", + "root": true +} diff --git a/app/manager/README.md b/app/manager/README.md new file mode 100644 index 00000000..58bf9348 --- /dev/null +++ b/app/manager/README.md @@ -0,0 +1,34 @@ +## Application Details +| | +| ------------- | +|**Generation Date and Time**
Fri Dec 22 2023 12:25:57 GMT+0530 (India Standard Time)| +|**App Generator**
@sap/generator-fiori-freestyle| +|**App Generator Version**
1.11.4| +|**Generation Platform**
Visual Studio Code| +|**Template Used**
simple| +|**Service Type**
Local Cap| +|**Service URL**
http://localhost:4004/odata/v4/manager/ +|**Module Name**
manager| +|**Application Title**
SLA Manager| +|**Namespace**
ns| +|**UI5 Theme**
sap_horizon| +|**UI5 Version**
1.120.3| +|**Enable Code Assist Libraries**
False| +|**Enable TypeScript**
True| +|**Add Eslint configuration**
False| + +## manager + +A Fiori application. + +### Starting the generated app + +- This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: + +http://localhost:4004/ns.manager/index.html + +#### Pre-requisites: + +1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) + + diff --git a/app/manager/annotations.cds b/app/manager/annotations.cds new file mode 100644 index 00000000..db3dfe3b --- /dev/null +++ b/app/manager/annotations.cds @@ -0,0 +1 @@ +using ManagerService as service from '../../srv/services'; \ No newline at end of file diff --git a/app/manager/package.json b/app/manager/package.json new file mode 100644 index 00000000..94883602 --- /dev/null +++ b/app/manager/package.json @@ -0,0 +1,33 @@ +{ + "name": "manager", + "version": "0.0.1", + "private": true, + "description": "A Fiori application.", + "keywords": [ + "ui5", + "openui5", + "sapui5" + ], + "main": "webapp/index.html", + "dependencies": {}, + "devDependencies": { + "@ui5/cli": "^3.0.0", + "@sap/ux-ui5-tooling": "1", + "@sapui5/types": "~1.120.0", + "ui5-tooling-transpile": "^3.2.0", + "typescript": "^5.1.6", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "@sap-ux/eslint-plugin-fiori-tools": "^0.2.0", + "@sap-ux/ui5-middleware-fe-mockserver": "2" + }, + "scripts": { + "build": "ui5 build --config=ui5.yaml --clean-dest --dest dist", + "lint": "eslint ./", + "ts-typecheck": "tsc --noEmit", + "prestart": "npm run ts-typecheck", + "prebuild": "npm run ts-typecheck", + "deploy": "fiori verify", + "deploy-config": "fiori add deploy-config" + } +} diff --git a/app/manager/tsconfig.json b/app/manager/tsconfig.json new file mode 100644 index 00000000..d8ba7f54 --- /dev/null +++ b/app/manager/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es2022", + "module": "es2022", + "skipLibCheck": true, + "allowJs": true, + "strict": true, + "strictPropertyInitialization": false, + "moduleResolution": "node", + "rootDir": "./webapp", + "outDir": "./dist", + "baseUrl": "./", + "paths": { + "ns/manager/*": [ + "./webapp/*" + ] + }, + "typeRoots": [ + "./node_modules/@types", + "./node_modules/@sapui5/types" + ] + }, + "include": [ + "./webapp/**/*" + ] +} \ No newline at end of file diff --git a/app/manager/ui5.yaml b/app/manager/ui5.yaml new file mode 100644 index 00000000..00c19e67 --- /dev/null +++ b/app/manager/ui5.yaml @@ -0,0 +1,28 @@ +specVersion: "3.1" +metadata: + name: ns.manager +type: application +server: + customMiddleware: + - name: fiori-tools-proxy + afterMiddleware: compression + configuration: + ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted + ui5: + path: + - /test-resources + url: https://ui5.sap.com + - name: fiori-tools-appreload + afterMiddleware: compression + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + configuration: + debug: true + excludePatterns: + - /Component-preload.js +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + debug: true diff --git a/app/manager/webapp/Component.ts b/app/manager/webapp/Component.ts new file mode 100644 index 00000000..9c5c1176 --- /dev/null +++ b/app/manager/webapp/Component.ts @@ -0,0 +1,28 @@ +import BaseComponent from "sap/ui/core/UIComponent"; +import { createDeviceModel } from "./model/models"; + +/** + * @namespace ns.manager + */ +export default class Component extends BaseComponent { + + public static metadata = { + manifest: "json" + }; + + /** + * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. + * @public + * @override + */ + public init() : void { + // call the base component's init function + super.init(); + + // enable routing + this.getRouter().initialize(); + + // set the device model + this.setModel(createDeviceModel(), "device"); + } +} \ No newline at end of file diff --git a/app/manager/webapp/controller/App.controller.ts b/app/manager/webapp/controller/App.controller.ts new file mode 100644 index 00000000..bd090f0e --- /dev/null +++ b/app/manager/webapp/controller/App.controller.ts @@ -0,0 +1,12 @@ +import Controller from "sap/ui/core/mvc/Controller"; + +/** + * @namespace ns.manager.controller + */ +export default class App extends Controller { + + /*eslint-disable @typescript-eslint/no-empty-function*/ + public onInit(): void { + + } +} \ No newline at end of file diff --git a/app/manager/webapp/controller/Main.controller.ts b/app/manager/webapp/controller/Main.controller.ts new file mode 100644 index 00000000..59de4610 --- /dev/null +++ b/app/manager/webapp/controller/Main.controller.ts @@ -0,0 +1,17 @@ +import Controller from "sap/ui/core/mvc/Controller"; + +/** + * @namespace ns.manager.controller + */ +export default class Main extends Controller { + + /*eslint-disable @typescript-eslint/no-empty-function*/ + public onInit(): void { + + } + + navToSpotStatus(event: GeoMap$KeyPressEvent) { + const spotIndex = event.getSource().getBindingContext("spotModel")?.getProperty("index"); + (this.getOwnerComponent() as UIComponent).getRouter().navTo("RouteSpotStatus", {index: spotIndex}); + } +} \ No newline at end of file diff --git a/app/manager/webapp/controller/SpotStatus.controller.ts b/app/manager/webapp/controller/SpotStatus.controller.ts new file mode 100644 index 00000000..a27d3c26 --- /dev/null +++ b/app/manager/webapp/controller/SpotStatus.controller.ts @@ -0,0 +1,41 @@ +import Controller from "sap/ui/core/mvc/Controller"; +import UIComponent from "sap/ui/core/UIComponent"; +import Filter from "sap/ui/model/Filter"; +import FilterOperator from "sap/ui/model/FilterOperator"; +import { IconTabBar$SelectEvent } from "sap/m/IconTabBar"; +import ListBinding from "sap/ui/model/ListBinding"; +import { Urgency } from "../format/util"; +/** + * @namespace ns.manager.controller + */ +export default class SpotStatus extends Controller { + + private statusFilters: Filter[] = []; + + public onInit() { + + } + + onFilterSelect(event: IconTabBar$SelectEvent): void { + + const listBinding = this.getView()?.byId("incidentList")?.getBinding("items") as ListBinding; + const key = (event.getParameter("key") as string); + + if (key === "L") { + this.statusFilters = [new Filter("urgency", FilterOperator.EQ, Urgency.Low, false)]; + } else if (key === "M") { + this.statusFilters = [new Filter("urgency", FilterOperator.EQ, Urgency.Medium, false)]; + } else if (key === "H") { + this.statusFilters = [new Filter("urgency", FilterOperator.EQ, Urgency.High, false)]; + } else { + this.statusFilters = []; + } + + listBinding.filter(this.statusFilters); + } + + navToMain() { + (this.getOwnerComponent() as UIComponent).getRouter().navTo("RouteMain"); + } + +} \ No newline at end of file diff --git a/app/manager/webapp/css/style.css b/app/manager/webapp/css/style.css new file mode 100644 index 00000000..f280a0e7 --- /dev/null +++ b/app/manager/webapp/css/style.css @@ -0,0 +1 @@ +/* Enter your custom styles here */ \ No newline at end of file diff --git a/app/manager/webapp/format/util.ts b/app/manager/webapp/format/util.ts new file mode 100644 index 00000000..26d170a9 --- /dev/null +++ b/app/manager/webapp/format/util.ts @@ -0,0 +1,25 @@ +import { MessageType } from "sap/ui/core/library"; + +export enum Urgency { + High = 'H', + Medium = 'M', + Low = 'L' +} + +export function formatHighlightColor(urgency: Urgency): MessageType { + + if (urgency === Urgency.High) return MessageType.Error; + else if (urgency === Urgency.Medium) return MessageType.Warning; + return MessageType.Information; + +} + + +export function formatDaysAgo(createdAt: Date): String { + const today = new Date() + const since = new Date(createdAt) + const diff = Math.abs(since.getTime() - today.getTime()); + const diffD = Math.ceil(diff / (1000 * 60 * 60 * 24)); + if (diffD <= 1) return `${diffD} day ago` + return `${diffD} days ago` +} \ No newline at end of file diff --git a/app/manager/webapp/i18n/i18n.properties b/app/manager/webapp/i18n/i18n.properties new file mode 100644 index 00000000..b42145c9 --- /dev/null +++ b/app/manager/webapp/i18n/i18n.properties @@ -0,0 +1,16 @@ +# This is the resource bundle for ns.manager + +#Texts for manifest.json + +#XTIT: Application name +appTitle=Incidents by Location + +#YDES: Application description +appDescription=A Fiori application. +#XTIT: Main view title +title=Incidents by Location +titleSpotStatus=Spot Status +msgFilterAll= All +msgFilterHigh= High +msgFilterMedium= Medium +msgFilterLow= Low \ No newline at end of file diff --git a/app/manager/webapp/index.html b/app/manager/webapp/index.html new file mode 100644 index 00000000..afb5f4af --- /dev/null +++ b/app/manager/webapp/index.html @@ -0,0 +1,35 @@ + + + + + + + SLA Manager + + + + +
+ + \ No newline at end of file diff --git a/app/manager/webapp/localData/spots.json b/app/manager/webapp/localData/spots.json new file mode 100644 index 00000000..14cf2535 --- /dev/null +++ b/app/manager/webapp/localData/spots.json @@ -0,0 +1,18 @@ +{ + "Spots": [ + { + "pos": "8.641622;49.293696;0", + "tooltip": "Walldorf", + "type": "Error", + "label": "ACME, Walldorf", + "index": "1" + }, + { + "pos": "77.714421;12.977788;0", + "tooltip": "Bangalore", + "type": "Error", + "label": "ACME, Bangalore", + "index": "2" + } + ] +} diff --git a/app/manager/webapp/localService/metadata.xml b/app/manager/webapp/localService/metadata.xml new file mode 100644 index 00000000..3167157f --- /dev/null +++ b/app/manager/webapp/localService/metadata.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/manager/webapp/manifest.json b/app/manager/webapp/manifest.json new file mode 100644 index 00000000..126ca431 --- /dev/null +++ b/app/manager/webapp/manifest.json @@ -0,0 +1,147 @@ +{ + "_version": "1.58.0", + "sap.app": { + "id": "ns.manager", + "type": "application", + "i18n": "i18n/i18n.properties", + "applicationVersion": { + "version": "0.0.1" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "resources": "resources.json", + "sourceTemplate": { + "id": "@sap/generator-fiori:basic", + "version": "1.11.4", + "toolsId": "8c4e6715-9768-45cc-b158-ab639c02ae80" + }, + "dataSources": { + "mainService": { + "uri": "/odata/v4/manager/", + "type": "OData", + "settings": { + "annotations": [], + "localUri": "localService/metadata.xml", + "odataVersion": "4.0" + } + }, + "spotService": { + "type": "JSON", + "uri": "./localData/spots.json" + } + } + }, + "sap.ui": { + "technology": "UI5", + "icons": { + "icon": "", + "favIcon": "", + "phone": "", + "phone@2": "", + "tablet": "", + "tablet@2": "" + }, + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "flexEnabled": false, + "dependencies": { + "minUI5Version": "1.120.3", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.f": {}, + "sap.suite.ui.generic.template": {}, + "sap.ui.comp": {}, + "sap.ui.generic.app": {}, + "sap.ui.table": {}, + "sap.ushell": {}, + "sap.ui.commons": {}, + "sap.ui.vbm": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ns.manager.i18n.i18n" + } + }, + "": { + "dataSource": "mainService", + "preload": true, + "settings": { + "synchronizationMode": "None", + "operationMode": "Server", + "autoExpandSelect": true, + "earlyRequests": true + } + }, + "spotModel":{ + "type": "sap.ui.model.json.JSONModel", + "dataSource": "spotService", + "preload": true + } + }, + "resources": { + "css": [ + { + "uri": "css/style.css" + } + ] + }, + "routing": { + "config": { + "routerClass": "sap.m.routing.Router", + "viewType": "XML", + "async": true, + "viewPath": "ns.manager.view", + "controlAggregation": "pages", + "controlId": "app", + "clearControlAggregation": false + }, + "routes": [ + { + "name": "RouteMain", + "pattern": ":?query:", + "target": [ + "TargetMain" + ] + }, + { + "name": "RouteSpotStatus", + "pattern": "spot/{index}", + "target": ["TargetSpotStatus"] + } + ], + "targets": { + "TargetMain": { + "viewType": "XML", + "transition": "slide", + "clearControlAggregation": false, + "viewId": "Main", + "viewName": "Main" + }, + "TargetSpotStatus": { + "viewId": "spotStatus", + "viewName": "SpotStatus", + "viewLevel": 2 + } + } + }, + "rootView": { + "viewName": "ns.manager.view.App", + "type": "XML", + "async": true, + "id": "App" + } + } +} diff --git a/app/manager/webapp/model/models.ts b/app/manager/webapp/model/models.ts new file mode 100644 index 00000000..16294c19 --- /dev/null +++ b/app/manager/webapp/model/models.ts @@ -0,0 +1,8 @@ +import JSONModel from "sap/ui/model/json/JSONModel"; +import Device from "sap/ui/Device"; + +export function createDeviceModel () { + const model = new JSONModel(Device); + model.setDefaultBindingMode("OneWay"); + return model; +} \ No newline at end of file diff --git a/app/manager/webapp/view/App.view.xml b/app/manager/webapp/view/App.view.xml new file mode 100644 index 00000000..0b12bbc7 --- /dev/null +++ b/app/manager/webapp/view/App.view.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/manager/webapp/view/Main.view.xml b/app/manager/webapp/view/Main.view.xml new file mode 100644 index 00000000..e1a92558 --- /dev/null +++ b/app/manager/webapp/view/Main.view.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/app/manager/webapp/view/SpotStatus.view.xml b/app/manager/webapp/view/SpotStatus.view.xml new file mode 100644 index 00000000..ae2c1e03 --- /dev/null +++ b/app/manager/webapp/view/SpotStatus.view.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + <Label text="{customer}" wrapping="true" /> + <Label text="{status}" wrapping="true" visible="false"/> + <ObjectNumber + + unit="{path: 'createdAt', formatter: 'util.formatDaysAgo'}"/> + </VBox> + + <Button + id="BadgedButton" + class="sapUiTinyMarginBeginEnd" + icon="sap-icon://comment" + type="Default" + enabled="false"> + <customData> + <BadgeCustomData + key="badge" + value="{comments}" + visible="true"/> + </customData> + </Button> + </HBox> + </f:GridListItem> + </f:items> + <f:noData> + <IllustratedMessage enableVerticalResponsiveness="true" illustrationType="sapIllus-EmptyList"/> + </f:noData> + </f:GridList> + </content> + </IconTabBar> + </content> + </Page> +</mvc:View> \ No newline at end of file diff --git a/app/services.cds b/app/services.cds index dc23bf1c..f380684e 100644 --- a/app/services.cds +++ b/app/services.cds @@ -1,2 +1,4 @@ -using from './incidents/annotations'; \ No newline at end of file +using from './incidents/annotations'; + +using from './manager/annotations'; \ No newline at end of file diff --git a/db/data/sap.capire.incidents-Incidents.conversation.csv b/db/data/sap.capire.incidents-Incidents.conversation.csv index bc40667d..0727b172 100644 --- a/db/data/sap.capire.incidents-Incidents.conversation.csv +++ b/db/data/sap.capire.incidents-Incidents.conversation.csv @@ -2,4 +2,4 @@ ID,up__ID,timestamp,author,message 2b23bb4b-4ac7-4a24-ac02-aa10cabd842c,3b23bb4b-4ac7-4a24-ac02-aa10cabd842c,1995-12-17T03:24:00Z,Harry John,Can you please check if battery connections are fine? 2b23bb4b-4ac7-4a24-ac02-aa10cabd843c,3a4ede72-244a-4f5f-8efa-b17e032d01ee,1995-12-18T04:24:00Z,Emily Elizabeth,Can you please check if there are any loose connections? 9583f982-d7df-4aad-ab26-301d4a157cd7,3583f982-d7df-4aad-ab26-301d4a157cd7,2022-09-04T12:00:00Z,Sunny Sunshine,Please check why the solar panel is broken -9583f982-d7df-4aad-ab26-301d4a158cd7,3ccf474c-3881-44b7-99fb-59a2a4668418,2022-09-04T13:00:00Z,Bradley Flowers,What exactly is wrong? +9583f982-d7df-4aad-ab26-301d4a158cd7,3ccf474c-3881-44b7-99fb-59a2a4668418,2022-09-04T13:00:00Z,Bradley Flowers,What exactly is wrong? \ No newline at end of file diff --git a/db/schema.cds b/db/schema.cds index fa96f9b9..756e51be 100644 --- a/db/schema.cds +++ b/db/schema.cds @@ -31,8 +31,8 @@ entity Addresses : cuid, managed { entity Incidents : cuid, managed { customer : Association to Customers; title : String @title: 'Title'; - urgency : Association to Urgency default 'M'; - status : Association to Status default 'N'; + urgency : Association to Urgency; + status : Association to Status; conversation : Composition of many { key ID : UUID; timestamp : type of managed:createdAt; diff --git a/package.json b/package.json index 5311e44b..86cd9183 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,14 @@ "axios": "^1.4.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", - "chai-subset": "^1.6.0" + "chai-subset": "^1.6.0", + "cds-plugin-ui5": "^0.6.13" }, "scripts": { "watch": "cds watch", "start": "cds-serve", - "test": "npx jest --silent" + "test": "npx jest --silent", + "watch-manager": "cds watch --open ns.manager/index.html?sap-ui-xx-viewCache=false --livereload false" }, "sapux": [ "app/incidents" @@ -52,5 +54,8 @@ } } }, - "private": true + "private": true, + "workspaces": [ + "app/*" + ] } diff --git a/srv/services.cds b/srv/services.cds index 60f15d85..533a9b0c 100644 --- a/srv/services.cds +++ b/srv/services.cds @@ -15,3 +15,18 @@ service AdminService @(requires:'admin') { entity Customers as projection on my.Customers; entity Incidents as projection on my.Incidents; } + +service ManagerService { + entity Incidents as select from my.Incidents, my.Incidents.conversation { + key Incidents.ID, + title, + customer.name as customer, + customer.email as email, + status.descr as status, + urgency.code as urgency, + createdAt, + count(Incidents.conversation.message) as comments: Integer + + } where Incidents.conversation.ID = conversation.ID + group by Incidents.ID +} \ No newline at end of file