diff --git a/README.md b/README.md
index 2ad454e..286df78 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,72 @@
-# Cumulocity widget plugin
-This is the Cumulocity module federation plugin. Plugins can be developed like any Cumulocity application, but can be used at runtime by other applications. Therefore, they export an Angular module which can then be imported by any other application. The exports are defined in `package.json`:
+# Route Tracker Widget for Cumulocity [](https://github.com/SoftwareAG/cumulocity-route-tracker-plugin/releases/download/1.0.0-beta/route-tracker-runtime-widget-1.0.0-beta.zip)
-```
-"exports": [
- {
- "name": "Example widget plugin",
- "module": "WidgetPluginModule",
- "path": "./widget/widget-plugin.module.ts",
- "description": "Adds custom widget"
- }
-]
-```
+
+This Route Tracker widget is the Cumulocity module federation plugin created using c8ycli. This plugin can be used in Application Builder or Cockpit.
+The Route Tracker widget help you to display route, geofence along with realtime device status and also one can enable the smart rule which gets trigged when smart rule violation happens.
-**How to start**
-Run the command below to scaffold a `widget` plugin.
+### Please note that this plugin is in currently under BETA mode.
-```
-c8ycli new widget-plugin
-```
+### Please choose Route Tracker release based on Cumulocity/Application builder version:
-As the app.module is a typical Cumuloctiy application, any new plugin can be tested via the CLI:
+|APPLICATION BUILDER | CUMULOCITY | ROUTE TRACKER WIDGET |
+|--------------------|------------|----------------------|
+| 2.x.x(coming soon) | >= 1016.x.x| 1.x.x |
-```
-npm start -- --shell cockpit
-```
+
+## Representation
-In the Module Federation terminology, `widget` plugin is called `remote` and the `cokpit` is called `shell`. Modules provided by this `widget` will be loaded by the `cockpit` application at the runtime. This plugin provides a basic custom widget that can be accessed through the `Add widget` menu.
+![RouteTrackermain](https://user-images.githubusercontent.com/24636020/186117425-55a2c67b-1dbf-47ba-b50f-5331e07c580a.PNG)
+
-> Note that the `--shell` flag creates a proxy to the cockpit application and provides` WidgetPluginModule` as an `remote` via URL options.
+## Prerequisite
+ Cumulocity c8ycli >=1016.x.x
+
+
+## Installation
-Also deploying needs no special handling and can be simply done via `npm run deploy`. As soon as the application has exports it will be uploaded as a plugin.
+### Runtime Widget Deployment?
+
+* This widget support runtime deployment. Download [Runtime Binary](https://github.com/SoftwareAG/cumulocity-route-tracker-plugin/releases/download/1.0.0-beta/route-tracker-runtime-widget-1.0.0-beta.zip) and install via Administrations(Beta mode) --> Ecosystems --> Applications --> Packages.
+
+
+
+## QuickStart
+This guide will teach you how to add widget in your existing or new dashboard.
+
+1. Open you application from App Switcher
+
+2. Add new dashboard or navigate to existing dashboard
+
+3. Click `Add Widget`
+
+4. Search for `Route Tracker`
+
+5. Select `Target Assets or Devices`
+
+7. Click `Save`
+
+Congratulations! Smart Map is configured.
+
+
+## User Guide
+
+![RouteTrackerConfiguration](https://user-images.githubusercontent.com/24636020/186117674-10d25550-ad94-4551-867a-12abd50bd847.PNG)
+
+1. Takes device name, geofence radius, start and end address/latitude and longitude, icon name color, marker color and smart rule configuration as input.
+
+2. If configured Smart rule with provided name doesnt exist then a new rule get created and if the rule with provided name exists then it updates the existing rule.
+**Note** Make sure for different devices and for differnt configuration a uinique smart rule name need to be provided else it overrides if the rule with name exists.
+
+To check the smart rules please navigate to Application Switcher -> Cockpit -> Configuration -> Global smart rule
+
+
+------------------------------
+
+This widget is provided as-is and without warranty or support. They do not constitute part of the Software AG product suite. Users are free to use, fork and modify them, subject to the license agreement. While Software AG welcomes contributions, we cannot guarantee to include every contribution in the master project.
+
+_____________________
+
+For more information you can Ask a Question in the [TECHcommunity Forums](https://tech.forums.softwareag.com/tags/c/forum/1/Cumulocity-IoT).
+
+You can find additional information in the [Software AG TECHcommunity](https://tech.forums.softwareag.com/tag/Cumulocity-IoT).
diff --git a/package-lock.json b/package-lock.json
index ef993dd..6c1aca6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -374,6 +374,14 @@
}
}
},
+ "@angular/material": {
+ "version": "14.2.7",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz",
+ "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==",
+ "requires": {
+ "tslib": "^2.3.0"
+ }
+ },
"@angular/platform-browser": {
"version": "14.0.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.0.6.tgz",
@@ -6234,6 +6242,17 @@
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
"dev": true
},
+ "@types/webpack": {
+ "version": "5.28.0",
+ "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz",
+ "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "tapable": "^2.2.0",
+ "webpack": "^5"
+ }
+ },
"@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
diff --git a/package.json b/package.json
index 2188573..6a99378 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"tslib": "^2.0.0",
"@ng-select/ng-select": "^9.1.0",
"@turf/turf": "^6.5.0",
- "font-awesome": "^4.7.0"
+ "font-awesome": "^4.7.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "14.0.6",
@@ -66,7 +66,6 @@
"del": "^6.1.1",
"style-loader": "3.3.1",
"html-loader": "4.1.0"
-
},
"c8y": {
"application": {
diff --git a/widget/gp-route-tracker.component.css b/widget/gp-route-tracker.component.css
index ba9e420..9f03f14 100644
--- a/widget/gp-route-tracker.component.css
+++ b/widget/gp-route-tracker.component.css
@@ -1,4 +1,9 @@
-leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
+@import '~@angular/material/prebuilt-themes/indigo-pink.css';
+@import '~leaflet2/dist/leaflet.css';
+@import '~leaflet-draw/dist/leaflet.draw.css';
+@import '~leaflet-extra-markers/dist/css/leaflet.extra-markers.min.css';
+
+.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
@@ -33,7 +38,29 @@ leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker
.marker-cluster-large div {
background-color: rgba(241, 128, 23, 0.6);
}
-
+
+ /* IE 6-8 fallback colors */
+.leaflet-oldie .marker-cluster-small {
+ background-color: rgb(181, 226, 140);
+ }
+.leaflet-oldie .marker-cluster-small div {
+ background-color: rgb(110, 204, 57);
+ }
+
+.leaflet-oldie .marker-cluster-medium {
+ background-color: rgb(241, 211, 87);
+ }
+.leaflet-oldie .marker-cluster-medium div {
+ background-color: rgb(240, 194, 12);
+ }
+
+.leaflet-oldie .marker-cluster-large {
+ background-color: rgb(253, 156, 115);
+ }
+.leaflet-oldie .marker-cluster-large div {
+ background-color: rgb(241, 128, 23);
+}
+
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
@@ -88,8 +115,11 @@ leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker
.lt-popup-row div {
/* flex: 2; */
- flex: none;
- color: #fff
+ /* flex: none; */
+ color: #fff;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.lt-actions-row {
@@ -98,15 +128,40 @@ leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker
padding: 5px;
}
+ .ng-select.am-select {
+ border: 0px;
+ min-height: 0px;
+ border-radius: 0;
+}
+
+.ng-select.am-select .ng-select-container {
+ min-height: 32px;
+ border-radius: 0;
+}
+
+.ng-select.ng-select-single.am-select .ng-select-container {
+ min-height: 32px;
+ height: 32px;
+ border-radius: 0px;
+}
+
+.pull-right {
+ text-align: right;
+}
+
+.pt-5 {
+ padding-top: 5px;
+}
/* Location tracking css end */
.extra-marker i.fa-rt {
- margin-top: -40px;
+ margin-top: -39px !important;
display: block;
- margin-left: -2px;
- font-size: 20px;
+ margin-left: -2px !important;
+ width: 35px;
+ text-align:center;
}
.leaflet-top,
diff --git a/widget/gp-route-tracker.component.ts b/widget/gp-route-tracker.component.ts
index 93b25bb..4221bb0 100644
--- a/widget/gp-route-tracker.component.ts
+++ b/widget/gp-route-tracker.component.ts
@@ -123,11 +123,7 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
async ngOnInit(){
await import("./gp-leaflet");
- L = window.L;
- console.log("ngOnInIt method....................................");
- console.log(window.L);
- console.log(L.Marker);
- // L = window.L;
+ L = window.L;
this.isflagged = 'true';
this.initializeMap(true);
}
@@ -180,12 +176,6 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
parseFloat(this.inputConfig.endLat),
parseFloat(this.inputConfig.endLng),
];
- if (isDevMode()) {
- console.log('routeStartPoint', this.routeStartPoint);
- }
- if (isDevMode()) {
- console.log('routeEndPoint', this.routeEndPoint);
- }
}
if (!isFirstCall) {
@@ -316,9 +306,7 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
this.addDevicesToMap(this.allDeviceList);
})
.catch((err) => {
- if (isDevMode()) {
console.log('+-+- ERROR FOUND WHILE GETTING CHILD DEVICES... ', err);
- }
});
} else {
this.allDeviceList.push(mo);
@@ -326,9 +314,7 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
}
})
.catch((err) => {
- if (isDevMode()) {
console.log('+-+- ERROR while getting context object details for dashboard ', err);
- }
});
} else {
if (this.allDeviceList.length > 0) {
@@ -462,18 +448,14 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
this.realTimeService.unsubscribe(detailSubs);
}
} catch (error) {
- if (isDevMode()) {
console.log('+-+-+- error while creating and adding marker to map\n ', [
error,
imo,
]);
- }
}
}
} else {
- if (isDevMode()) {
console.log('+-+- device without location\n', imo);
- }
}
});
if (categoryFeatureGroups.length > 0) {
@@ -496,12 +478,9 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
* This method is used to create marker for given device
*/
private createMarker(mo: IManagedObject): any {
- if (isDevMode()) {
- console.log('Input config', this.inputConfig);
- }
// add floor plan, stored in the position's altitude, as option in the marker for later comparisons...
const iconMarker = L.ExtraMarkers.icon({
- icon: 'fa-' + this.inputConfig.iconName, // 'fa-truck'
+ icon: this.__getIconForType(mo), // 'fa-truck'
iconColor: this.inputConfig.iconColor, // this.markerFontColor,'yellow'
iconSize: [200, 200], // size of the icon
extraClasses: 'fa-rt',
@@ -510,9 +489,6 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
svg: 'false',
prefix: 'fa',
});
- if (isDevMode()) {
- console.log('iconMarker', iconMarker);
- }
const iconOpts = {
title: mo.name,
id: mo.id,
@@ -520,9 +496,6 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
draggable: false,
};
const markerLatLng = L.latLng(mo.c8y_Position);
- console.log("createMarker....................................");
- console.log(window.L);
- console.log(L.Marker);
const mkr = L.Marker.movingMarker([markerLatLng, markerLatLng], [1000], iconOpts);
const mpp = L.popup({ className: 'lt-popup' });
const elem = [
@@ -562,15 +535,22 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
return mkr;
}
+ /**
+ * Returns the Font Awesome icon class to be used depending on the device type retrieved from Cumulocity
+ */
+ private __getIconForType(mo: any): string {
+ if (this.config.iconName) {
+ return `dlt-c8y-icon-${this.config.iconName}`;
+ }
+ return 'fa-asterisk';
+}
+
/**
* Render geofence on map
*/
private async __doRenderGeofencesOnMap(e: any): Promise {
const route = e.route;
// Do something with the route here
- if (isDevMode()) {
- console.log(this.isflagged);
- }
if (this.isflagged === 'true') {
if (route && route.coordinates) {
route.coordinates.forEach((element) => {
@@ -768,9 +748,6 @@ export class GpRouteTrackerComponent implements OnInit, AfterViewInit {
.then(async (smartRulesList) => {
const smartRuleRecord = smartRulesList.rules.find((rule) => rule.name === smartRuleName);
if (smartRuleRecord) {
- if (isDevMode()) {
- console.log('smartRuleRecord inner', smartRuleRecord);
- }
this.updateSmartRule(smartRuleRecord.id);
} else {
this.creatSmartRule();
diff --git a/widget/route-tracker-config/gp-route-tracker-config.component.ts b/widget/route-tracker-config/gp-route-tracker-config.component.ts
index 804c7b0..604e7a7 100644
--- a/widget/route-tracker-config/gp-route-tracker-config.component.ts
+++ b/widget/route-tracker-config/gp-route-tracker-config.component.ts
@@ -77,17 +77,11 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
}
this.suggestions$ = new Observable((observer: Observer) => {
this.locationSearchAPI.searchGeoLocation(this.value).subscribe((res: any) => {
- if (isDevMode()) {
- console.log(res);
- }
observer.next(res);
});
});
this.routeEndSuggestions$ = new Observable((observer: Observer) => {
this.locationSearchAPI.searchGeoLocation(this.routeEndvalue).subscribe((res: any) => {
- if (isDevMode()) {
- console.log(res);
- }
observer.next(res);
});
});
@@ -107,9 +101,6 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
// Update the icon colors input from color input box
colorUpdateByTyping(colorTyped): void {
- if (isDevMode()) {
- console.log('typed', colorTyped);
- }
this.iconColorCode = colorTyped;
}
// Update the marker colors input from color picker
@@ -119,15 +110,9 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
// Update the marker colors input from color input box
markerColorUpdateByTyping(colorTyped): void {
- if (isDevMode()) {
- console.log('typed', colorTyped);
- }
this.markerColorCode= colorTyped;
}
calledthis(address): void {
- if (isDevMode()) {
- console.log('Address => ', this.config.address, address);
- }
}
changeTypeaheadLoading(e: boolean): void {
this.isLoading = e;
@@ -136,9 +121,6 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
* Change map coordinates based on location search API output
*/
displayFn(searchResult: LocationResult): void {
- if (isDevMode()) {
- console.log('searchResult', searchResult);
- }
if (searchResult) {
this.config.startLat = searchResult.lat;
this.config.startLng = searchResult.lon;
@@ -148,9 +130,6 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
}
}
displayFunc(searchResult: LocationResult): void {
- if (isDevMode()) {
- console.log('searchResult', searchResult);
- }
if (searchResult) {
this.config.endLat = searchResult.lat;
this.config.endLng = searchResult.lon;
@@ -202,9 +181,6 @@ export class GpRouteTrackerConfigComponent implements OnInit, DoCheck {
fgOnLvl.layer.bringToFront();
this.layerControl.addOverlay(fgOnLvl.layer, fgOnLvl.name);
this.layerControl.addTo(this.map);
- if (isDevMode()) {
- console.log('geoFence => => ', this.allGoefences, geoFence);
- }
}
async ngDoCheck(): Promise {