aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/odlux/framework')
-rw-r--r--sdnr/wt/odlux/framework/pom.xml5
-rw-r--r--sdnr/wt/odlux/framework/src/actions/settingsAction.ts8
-rw-r--r--sdnr/wt/odlux/framework/src/app.tsx7
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/About.svg18
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/Home.svg28
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/Menu.svg18
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/Tools.svg35
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/User.svg21
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.pngbin0 -> 14989 bytes
-rw-r--r--sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg81
-rw-r--r--sdnr/wt/odlux/framework/src/components/errorDisplay.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx29
-rw-r--r--sdnr/wt/odlux/framework/src/components/logo.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/index.tsx16
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx6
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-table/utilities.ts9
-rw-r--r--sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx14
-rw-r--r--sdnr/wt/odlux/framework/src/components/navigationMenu.tsx27
-rw-r--r--sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/components/settings/general.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/components/titleBar.tsx27
-rw-r--r--sdnr/wt/odlux/framework/src/flux/connect.tsx (renamed from sdnr/wt/odlux/framework/src/flux/connect.ts)94
-rw-r--r--sdnr/wt/odlux/framework/src/flux/store.ts16
-rw-r--r--sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts2
-rw-r--r--sdnr/wt/odlux/framework/src/middleware/logger.ts7
-rw-r--r--sdnr/wt/odlux/framework/src/middleware/navigation.ts2
-rw-r--r--sdnr/wt/odlux/framework/src/services/applicationApi.ts5
-rw-r--r--sdnr/wt/odlux/framework/src/services/applicationManager.ts4
-rw-r--r--sdnr/wt/odlux/framework/src/services/broadcastService.ts146
-rw-r--r--sdnr/wt/odlux/framework/src/services/index.ts2
-rw-r--r--sdnr/wt/odlux/framework/src/services/restService.ts54
-rw-r--r--sdnr/wt/odlux/framework/src/services/storeService.ts11
-rw-r--r--sdnr/wt/odlux/framework/src/services/userdataService.ts (renamed from sdnr/wt/odlux/framework/src/services/settingsService.ts)4
-rw-r--r--sdnr/wt/odlux/framework/src/store/applicationStore.ts6
-rw-r--r--sdnr/wt/odlux/framework/src/utilities/logLevel.ts8
-rw-r--r--sdnr/wt/odlux/framework/src/views/about.tsx78
-rw-r--r--sdnr/wt/odlux/framework/src/views/frame.tsx143
-rw-r--r--sdnr/wt/odlux/framework/src/views/home.tsx33
-rw-r--r--sdnr/wt/odlux/framework/src/views/login.tsx320
-rw-r--r--sdnr/wt/odlux/framework/src/views/settings.tsx45
-rw-r--r--sdnr/wt/odlux/framework/src2/main/resources/version.json2
41 files changed, 804 insertions, 543 deletions
diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml
index 414f49827..3dd0f1a9d 100644
--- a/sdnr/wt/odlux/framework/pom.xml
+++ b/sdnr/wt/odlux/framework/pom.xml
@@ -19,6 +19,7 @@
~ ============LICENSE_END=======================================================
~
-->
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -45,7 +46,7 @@
<properties>
<buildtime>${maven.build.timestamp}</buildtime>
<distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
- <buildno>142.63ceae1(22/01/31)</buildno>
+ <buildno>178.3bd8a2a9(23/03/16)</buildno>
<odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
</properties>
@@ -102,7 +103,7 @@
<!-- optional: default phase is "generate-resources" -->
<phase>initialize</phase>
<configuration>
- <nodeVersion>v12.13.0</nodeVersion>
+ <nodeVersion>v12.22.0</nodeVersion>
<yarnVersion>v1.22.10</yarnVersion>
</configuration>
</execution>
diff --git a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts
index 1c18ddc8e..092b31814 100644
--- a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts
+++ b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts
@@ -19,7 +19,7 @@
import { Dispatch } from "../flux/store";
import { Action } from "../flux/action";
import { GeneralSettings, Settings, TableSettings, TableSettingsColumn } from "../models/settings";
-import { getSettings, putSettings } from "../services/settingsService";
+import { getUserdata, saveUserdata } from "../services/userdataService";
import { startWebsocketSession, suspendWebsocketSession } from "../services/notificationService";
import { IApplicationStoreState } from "../store/applicationStore";
@@ -67,7 +67,7 @@ export const setGeneralSettingsAction = (value: boolean) => (dispatcher: Dispatc
export const updateGeneralSettingsAction = (activateNotifications: boolean) => async (dispatcher: Dispatch) => {
const value: GeneralSettings = { general: { areNotificationsEnabled: activateNotifications } };
- const result = await putSettings("/general", JSON.stringify(value.general));
+ const result = await saveUserdata("/general", JSON.stringify(value.general));
dispatcher(setGeneralSettingsAction(activateNotifications));
}
@@ -86,14 +86,14 @@ export const updateTableSettings = (tableName: string, columns: TableSettingsCol
// would only save latest entry
//const json = JSON.stringify({ [tableName]: { columns: columns } });
- const result = await putSettings("/tables", json);
+ const result = await saveUserdata("/tables", json);
dispatcher(new SetTableSettings(tableName, columns));
}
export const getGeneralSettingsAction = () => async (dispatcher: Dispatch) => {
- const result = await getSettings<GeneralSettings>();
+ const result = await getUserdata<GeneralSettings>();
if (result && result.general) {
dispatcher(new SetGeneralSettingsAction(result.general.areNotificationsEnabled!))
diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx
index 9b03a216d..bbe1f9ec8 100644
--- a/sdnr/wt/odlux/framework/src/app.tsx
+++ b/sdnr/wt/odlux/framework/src/app.tsx
@@ -32,14 +32,15 @@ import { applicationStoreCreator } from './store/applicationStore';
import { ApplicationStoreProvider } from './flux/connect';
import { startHistoryListener } from './middleware/navigation';
-import { startRestService } from './services/restService';
+import { startSoreService } from './services/storeService';
import { startUserSessionService } from './services/userSessionService';
import { startNotificationService } from './services/notificationService';
+import { startBroadcastChannel } from './services/broadcastService';
+
import theme from './design/default';
import '!style-loader!css-loader!./app.css';
-import { startBroadcastChannel } from './services/broadcastService';
declare module '@mui/material/styles' {
@@ -98,7 +99,7 @@ export const runApplication = () => {
};
- startRestService(applicationStore);
+ startSoreService(applicationStore);
startHistoryListener(applicationStore);
startNotificationService(applicationStore);
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/About.svg b/sdnr/wt/odlux/framework/src/assets/icons/About.svg
new file mode 100644
index 000000000..156e36efe
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/About.svg
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+
+<path fill="#36A9E1" d="M 13 13 V 17.5 A 1 1 0 0 1 11 17.5 V 13 A 1 1 0 0 1 13 13 Z "/>
+
+<path fill="#36A9E1" d="M 12 11 A 1 1 0 1 0 11 10 A 1 1 0 0 0 12 11 Z "/>
+
+<path fill="#565656" d="M 1.125 20.485 A 1 1 0 0 1 1.152 19.47 L 11.152 3.47 A 1.039 1.039 0 0 1 12.852 3.47 L 22.852 19.47 A 1 1 0 0 1 22 21 H 2 A 1 1 0 0 1 1.125 20.485 Z M 3.8 19 H 20.2 L 12 5.887 Z"/>
+
+</svg> \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Home.svg b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg
new file mode 100644
index 000000000..0836714b4
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg
@@ -0,0 +1,28 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 460.298 460.297" style="enable-background:new 0 0 460.298 460.297;"
+ xml:space="preserve">
+<g>
+ <g>
+ <path fill="#565656" d="M230.149,120.939L65.986,256.274c0,0.191-0.048,0.472-0.144,0.855c-0.094,0.38-0.144,0.656-0.144,0.852v137.041
+ c0,4.948,1.809,9.236,5.426,12.847c3.616,3.613,7.898,5.431,12.847,5.431h109.63V303.664h73.097v109.64h109.629
+ c4.948,0,9.236-1.814,12.847-5.435c3.617-3.607,5.432-7.898,5.432-12.847V257.981c0-0.76-0.104-1.334-0.288-1.707L230.149,120.939
+ z"/>
+ <path fill="#565656" d="M457.122,225.438L394.6,173.476V56.989c0-2.663-0.856-4.853-2.574-6.567c-1.704-1.712-3.894-2.568-6.563-2.568h-54.816
+ c-2.666,0-4.855,0.856-6.57,2.568c-1.711,1.714-2.566,3.905-2.566,6.567v55.673l-69.662-58.245
+ c-6.084-4.949-13.318-7.423-21.694-7.423c-8.375,0-15.608,2.474-21.698,7.423L3.172,225.438c-1.903,1.52-2.946,3.566-3.14,6.136
+ c-0.193,2.568,0.472,4.811,1.997,6.713l17.701,21.128c1.525,1.712,3.521,2.759,5.996,3.142c2.285,0.192,4.57-0.476,6.855-1.998
+ L230.149,95.817l197.57,164.741c1.526,1.328,3.521,1.991,5.996,1.991h0.858c2.471-0.376,4.463-1.43,5.996-3.138l17.703-21.125
+ c1.522-1.906,2.189-4.145,1.991-6.716C460.068,229.007,459.021,226.961,457.122,225.438z"/>
+
+<path fill="#246DA2" d="M 457.122 225.438 L 251.849 54.417 L 251.849 54.417 C 245.765 49.468 238.531 46.994 230.155 46.994 C 221.78 46.994 214.547 49.468 208.457 54.417 L 3.172 225.438 C 1.269 226.958 0.226 229.004 0.032 231.574 C -0.161 234.142 0.504 236.385 2.029 238.287 L 19.73 259.415 C 21.255 261.127 23.251 262.174 25.726 262.557 C 28.011 262.749 30.296 262.081 32.581 260.559 L 230.149 95.817 L 427.719 260.558 C 429.245 261.886 431.24 262.549 433.715 262.549 H 434.573 C 437.044 262.173 439.036 261.119 440.569 259.411 L 458.272 238.286 C 459.794 236.38 460.461 234.141 460.263 231.57 C 460.068 229.007 459.021 226.961 457.122 225.438 Z"/>
+ </g>
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg
new file mode 100644
index 000000000..ea0312802
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg
@@ -0,0 +1,18 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+<path fill="#36A9E1" d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z"/>
+
+<path fill="#36A9E1" d="M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z"/>
+
+<path fill="#36A9E1" d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z"/>
+
+</svg> \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg
new file mode 100644
index 000000000..1595cdc1c
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg
@@ -0,0 +1,35 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 265 265">
+
+<g transform="translate(0.000000,265.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M601 2515 c-35 -8 -66 -17 -69 -20 -3 -3 76 -87 175 -188 243 -244
+243 -246 59 -425 -106 -103 -137 -120 -194 -107 -19 4 -88 66 -207 185 -99 99
+-183 180 -186 180 -17 0 -34 -104 -33 -200 0 -82 6 -120 23 -170 61 -174 191
+-306 362 -367 62 -22 93 -27 187 -28 l113 -2 602 -600 c673 -671 633 -638 777
+-638 93 1 151 27 225 101 99 98 129 219 85 339 -21 57 -49 87 -626 665 l-603
+605 5 70 c18 217 -117 451 -316 549 -127 62 -250 78 -379 51z m1696 -1906 c70
+-34 113 -125 93 -199 -31 -116 -158 -168 -262 -107 -77 45 -107 133 -73 214
+21 50 42 72 90 94 53 24 99 24 152 -2z"/>
+
+<path fill="#565656" d="M2183 2496 c-23 -7 -56 -23 -75 -34 -18 -12 -172 -161 -343 -331
+l-310 -311 45 -45 c24 -25 49 -45 55 -45 5 0 152 142 325 315 264 264 319 315
+340 313 19 -2 26 -10 28 -32 3 -26 -26 -59 -315 -348 l-318 -318 38 -37 37
+-38 319 318 c287 287 321 318 347 315 24 -2 30 -8 32 -32 3 -26 -26 -59 -315
+-348 l-318 -318 48 -47 47 -48 320 320 c225 225 327 333 342 365 31 66 30 159
+-4 225 -66 127 -210 198 -325 161z"/>
+
+<path fill="#36A9E1" d="M800 875 l-175 -175 -85 0 -85 0 -135 -222 c-74 -121 -137 -225 -139
+-231 -4 -11 89 -107 104 -107 5 0 109 60 230 134 l220 133 3 84 3 84 177 178
+177 177 -60 60 -60 60 -175 -175z"/>
+
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/User.svg b/sdnr/wt/odlux/framework/src/assets/icons/User.svg
new file mode 100644
index 000000000..99618cf57
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/User.svg
@@ -0,0 +1,21 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 600">
+
+ <defs>
+ <clipPath id="circular-border">
+ <circle cx="300" cy="300" r="250" />
+ </clipPath>
+ </defs>
+
+ <circle fill="#36A9E1" cx="300" cy="300" r="280"/>
+ <circle fill="#ffffff" cx="300" cy="230" r="100" />
+ <circle fill="#ffffff" cx="300" cy="550" r="190" clip-path="url(#circular-border)" />
+</svg> \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png
new file mode 100644
index 000000000..412390c79
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png
Binary files differ
diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg
new file mode 100644
index 000000000..c1d74bc96
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg
@@ -0,0 +1,81 @@
+<!-- highstreet technologies GmbH colour scheme
+ Grey #565656
+ LBlue #36A9E1
+ DBlue #246DA2
+ Green #003F2C / #006C4B
+ Yellw #C8D400
+ Red #D81036
+-->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 795.000000 139.000000">
+
+<g transform="translate(0.000000,139.000000) scale(0.100000,-0.100000)">
+
+<path fill="#565656" d="M355 1163 c-38 -2 -75 -9 -82 -15 -10 -8 -13 -53 -13 -175 0 -175 -9
+-215 -50 -226 -17 -4 -20 -13 -20 -59 0 -48 3 -56 32 -80 l33 -27 3 -190 c4
+-226 -4 -213 135 -209 l92 3 0 70 0 70 -32 3 -33 3 0 148 c0 135 -2 151 -21
+175 l-20 26 20 26 c19 24 21 40 21 170 l0 143 33 3 32 3 0 70 0 70 -30 1 c-16
+0 -61 -1 -100 -3z"/>
+
+<path fill="#565656" d="M7473 1154 c-2 -6 -3 -38 -1 -70 3 -59 3 -59 36 -62 l32 -3 0 -137
+c0 -119 3 -141 20 -169 20 -33 20 -33 0 -65 -17 -29 -20 -51 -20 -175 l0 -142
+-32 -3 -33 -3 0 -70 0 -70 97 -3 c141 -4 138 -9 138 212 0 96 3 182 6 191 4 9
+17 21 30 26 21 8 24 15 24 67 0 53 -3 60 -30 80 l-30 23 0 172 c0 123 -4 178
+-12 189 -16 20 -217 31 -225 12z"/>
+
+<path fill="#246DA2" d="M645 1138 c-3 -7 -4 -215 -3 -463 l3 -450 90 0 90 0 0 195 c0 215 5
+237 65 280 71 50 146 45 178 -13 14 -27 17 -64 19 -247 l2 -215 88 0 88 0 0
+250 0 250 -27 55 c-33 67 -71 97 -138 110 -89 17 -181 -12 -240 -75 l-25 -27
+-5 179 -5 178 -88 3 c-64 2 -89 -1 -92 -10z"/>
+
+<path fill="#C8D400" d="M2499 1111 c-150 -48 -271 -193 -301 -361 -32 -184 85 -406 259 -492
+165 -81 408 -24 514 120 31 43 31 44 13 61 -10 10 -45 33 -76 50 l-57 32 -15
+-28 c-75 -145 -315 -142 -403 6 -135 226 18 510 249 462 69 -15 114 -42 139
+-86 12 -19 26 -35 31 -35 22 0 138 91 132 103 -89 153 -301 227 -485 168z"/>
+
+<path fill="#246DA2" d="M1467 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -42 -3 -43 -3 0 -70 0 -70
+43 -3 42 -3 0 -197 c0 -185 1 -201 23 -242 26 -53 89 -90 152 -90 45 0 171 32
+187 48 7 7 4 30 -9 74 -16 52 -23 62 -39 60 -73 -11 -101 -12 -114 -3 -12 8
+-16 43 -18 180 l-3 170 68 3 68 3 0 70 0 70 -67 3 -67 3 -3 102 -3 102 -80 3
+c-45 1 -84 0 -88 -4z"/>
+
+<path fill="#C8D400" d="M7017 1094 c-4 -4 -7 -51 -7 -105 l0 -98 -37 -3 -38 -3 0 -70 0 -70
+36 -3 36 -3 7 -67 c3 -37 6 -130 6 -208 0 -130 2 -144 23 -175 27 -41 98 -79
+147 -79 36 0 120 19 172 39 l26 10 -15 58 c-19 74 -28 85 -59 72 -35 -13 -88
+-11 -102 3 -8 8 -12 63 -12 180 l0 167 68 3 67 3 0 70 0 70 -67 3 -67 3 -3
+102 -3 102 -85 3 c-47 1 -89 0 -93 -4z"/>
+
+<path fill="#C8D400" d="M3349 890 c-63 -11 -123 -43 -174 -95 -55 -54 -82 -106 -94 -181 -25
+-161 50 -304 198 -372 77 -36 211 -38 285 -4 75 34 129 84 167 154 32 59 34
+69 34 158 0 82 -4 103 -26 151 -46 98 -138 168 -249 189 -59 11 -75 11 -141 0z
+m168 -178 c44 -31 67 -86 67 -157 0 -113 -53 -176 -153 -183 -70 -5 -115 22
+-148 88 -53 103 -11 233 87 273 31 13 117 1 147 -21z"/>
+
+<path fill="#C8D400" d="M4254 890 c-61 -13 -106 -34 -147 -69 l-36 -32 -3 48 -3 48 -80 0
+-80 0 0 -330 0 -330 85 0 85 0 5 202 c5 184 7 204 26 229 45 60 111 88 171 72
+54 -14 58 -31 63 -278 l5 -225 85 0 85 0 0 266 0 266 -30 48 c-46 74 -132 106
+-231 85z"/>
+
+<path fill="#C8D400" d="M5040 890 c-63 -11 -89 -23 -142 -63 l-47 -36 -3 47 -3 47 -80 0 -80
+0 0 -330 0 -330 90 0 90 0 1 196 c1 158 4 201 18 226 37 73 151 111 205 69 28
+-22 35 -68 40 -276 l6 -215 85 0 85 0 0 245 c-1 264 -7 307 -51 360 -47 55
+-122 76 -214 60z"/>
+
+<path fill="#C8D400" d="M5709 890 c-99 -17 -200 -94 -250 -190 -21 -39 -24 -59 -24 -145 0
+-90 3 -105 28 -153 79 -149 241 -220 407 -178 79 21 145 61 188 118 39 51 34
+58 -54 77 -56 13 -56 13 -88 -16 -17 -16 -44 -35 -59 -41 -42 -18 -117 -15
+-157 7 -33 17 -79 81 -80 108 0 10 60 13 253 15 l252 3 -1 70 c-2 211 -197
+364 -415 325z m151 -150 c36 -19 80 -79 80 -110 0 -6 -56 -10 -160 -10 -149 0
+-160 1 -160 19 0 29 43 85 80 103 46 24 112 23 160 -2z"/>
+
+<path fill="#C8D400" d="M6483 890 c-186 -39 -303 -204 -273 -386 19 -114 95 -214 200 -263
+46 -22 69 -26 145 -26 76 0 99 4 145 26 65 30 125 80 146 120 17 33 15 35 -84
+64 l-62 18 -28 -25 c-40 -38 -65 -48 -120 -48 -91 0 -162 80 -162 182 0 84 36
+148 99 174 65 27 133 15 176 -32 l25 -27 80 23 c44 13 83 26 85 30 6 10 -42
+71 -82 104 -68 58 -194 86 -290 66z"/>
+
+<path fill="#36A9E1" d="M1934 407 c-2 -7 -4 -52 -2 -98 l3 -84 70 0 70 0 0 95 0 95 -68 3
+c-50 2 -69 -1 -73 -11z"/>
+
+</g>
+</svg>
diff --git a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
index a04ab16af..d41d82687 100644
--- a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
+++ b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx
@@ -15,7 +15,7 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React from 'react';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
@@ -31,7 +31,7 @@ import Typography from '@mui/material/Typography';
import { ClearErrorInfoAction, RemoveErrorInfoAction } from '../actions/errorActions';
-import connect, { Connect } from '../flux/connect';
+import { connect, Connect } from '../flux/connect';
const styles = (theme: Theme) => createStyles({
modal: {
diff --git a/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx
new file mode 100644
index 000000000..0d7d734c9
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+type MenuIconPropsBase = {
+ className?: string;
+ size?: number | string;
+};
+
+type MenuIconPropsWithColor = MenuIconPropsBase & {
+ color: string;
+};
+
+type MenuIconProps = MenuIconPropsBase | MenuIconPropsWithColor;
+
+const MenuIcon = (props: MenuIconProps) => {
+ const { className, size = '30px' } = props;
+ const color = 'color' in props ? props.color : '#36A9E1';
+
+ return (
+ <svg className={className} width={size} height={size} viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
+ <path fill={color} d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z" />
+ <path fill={color} d="M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z" />
+ <path fill={color} d="M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z" />
+ </svg>
+ );
+};
+
+MenuIcon.defaultName = 'MenuIcon';
+
+export default MenuIcon; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/components/logo.tsx b/sdnr/wt/odlux/framework/src/components/logo.tsx
index b10cc8ce1..f2bb1f575 100644
--- a/sdnr/wt/odlux/framework/src/components/logo.tsx
+++ b/sdnr/wt/odlux/framework/src/components/logo.tsx
@@ -42,7 +42,7 @@ import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
-import defaultLogo from '../assets/images/defaultLogo.svg';
+const defaultLogo = require('../assets/icons/ht.Connect.svg');
const styles = (theme: Theme) => createStyles({
headerLogo: {
@@ -91,7 +91,7 @@ class LogoComponent extends React.Component<LogoProps, ILogoState> {
console.info([
"Logo hidden, because browser window width (",
this.state.windowWidth,
- "px) is lower thershold (",
+ "px) is lower threshold (",
this.hideLogoWhenWindowWidthIsLower,
"px)."].join(''));
}
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
index 8541cfe56..c1a5005d4 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
@@ -450,13 +450,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
if (filterExpressionAsString.length === 0 || isNaN(valueAsNumber)) return true;
if (filterExpressionAsString.startsWith('>=')) {
- return valueAsNumber >= Number(filterExpressionAsString.substr(2).trim());
+ return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim());
} else if (filterExpressionAsString.startsWith('<=')) {
- return valueAsNumber <= Number(filterExpressionAsString.substr(2).trim());
+ return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim());
} else if (filterExpressionAsString.startsWith('>')) {
- return valueAsNumber > Number(filterExpressionAsString.substr(1).trim());
+ return valueAsNumber > Number(filterExpressionAsString.substring(1).trim());
} else if (filterExpressionAsString.startsWith('<')) {
- return valueAsNumber < Number(filterExpressionAsString.substr(1).trim());
+ return valueAsNumber < Number(filterExpressionAsString.substring(1).trim());
}
} else if (column.type === ColumnType.date){
const valueAsString = String(dataValue);
@@ -480,13 +480,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
const filterExpressionAsString = String(filterExpression).trim();
if (filterExpressionAsString.startsWith('>=')) {
- return valueAsDate >= convertToDate(filterExpressionAsString.substr(2).trim());
+ return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim());
} else if (filterExpressionAsString.startsWith('<=')) {
- return valueAsDate <= convertToDate(filterExpressionAsString.substr(2).trim());
+ return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim());
} else if (filterExpressionAsString.startsWith('>')) {
- return valueAsDate > convertToDate(filterExpressionAsString.substr(1).trim());
+ return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim());
} else if (filterExpressionAsString.startsWith('<')) {
- return valueAsDate < convertToDate(filterExpressionAsString.substr(1).trim());
+ return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim());
}
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx
index f8ae6ea97..ab0d465e7 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx
@@ -16,9 +16,9 @@
* ============LICENSE_END==========================================================================
*/
-import { Button, Checkbox, FormControlLabel, MenuItem, Popover, Switch, Typography } from '@mui/material';
-import connect, { Connect, IDispatcher } from '../../flux/connect';
-import * as React from 'react';
+import React from 'react';
+import { Button, FormControlLabel, Popover, Switch, Typography } from '@mui/material';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
import { ColumnModel } from './columnModel';
import { IApplicationStoreState } from '../../store/applicationStore';
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
index f9015493f..e2fda7647 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
+++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
@@ -51,6 +51,7 @@ export type ExternalMethodes<TData> = {
onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
onHideColumns: (columnName: string[]) => void;
onShowColumns: (columnName: string[]) => void;
+ onClearFilters: () => void;
},
createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => {
onPreFilterChanged: (preFilter: {
@@ -328,7 +329,13 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState
dispatch((dispatch: Dispatch) => {
dispatch(new ShowColumnsAction(columnName));
})
- }
+ },
+ onClearFilters: () => {
+ dispatch((dispatch: Dispatch) => {
+ let filter = { };
+ dispatch(new SetFilterChangedAction(filter));
+ });
+ },
// selected:
};
};
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
index 744cb0d24..626cb8978 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
@@ -27,6 +27,8 @@ import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
import { toAriaLabel } from '../../utilities/yangHelper';
+import { IconType } from '../../models/iconDefinition';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const styles = (theme: Theme) => createStyles({
active: {
@@ -35,7 +37,7 @@ const styles = (theme: Theme) => createStyles({
});
export interface IListItemLinkProps extends WithStyles<typeof styles> {
- icon: JSX.Element | null;
+ icon: IconType | null;
primary: string | React.ComponentType;
secondary?: React.ComponentType;
to: string;
@@ -48,13 +50,19 @@ export const ListItemLink = withStyles(styles)((props: IListItemLinkProps) => {
const renderLink = (itemProps: any): JSX.Element => (
props.external ? <a target="_blank" href={to} { ...itemProps }></a> :
<NavLink exact={ exact } to={ to } activeClassName={ classes.active } { ...itemProps } />);
-
+
+ const customIconHeight = 22;
const ariaLabel = typeof Primary === 'string' ? toAriaLabel("link-to-"+Primary) : toAriaLabel("link-to-"+Primary.displayName);
+
+ //create menu icon, either using an faIcon or a link to a custom svg icon
+ //moved to one place for easier usage
+ const listItemIcon = icon && ( typeof icon === 'string' ? <img height={customIconHeight} src={icon} /> : <FontAwesomeIcon icon={icon} /> );
+
return (
<>
<ListItem button component={ renderLink } aria-label={ariaLabel}>
{ icon
- ? <ListItemIcon>{ icon }</ListItemIcon>
+ ? <ListItemIcon>{ listItemIcon }</ListItemIcon>
: null
}
{ typeof Primary === 'string'
diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
index 195706d28..d969286b7 100644
--- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
+++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
@@ -15,15 +15,14 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React from 'react';
import { Theme } from '@mui/material/styles';
+import classNames from 'classnames';
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
-import { faHome, faAddressBook } from '@fortawesome/free-solid-svg-icons';
-
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
@@ -34,11 +33,14 @@ import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
import ListItemLink from '../components/material-ui/listItemLink';
-import connect, { Connect } from '../flux/connect';
+import { connect, Connect } from '../flux/connect';
import { MenuAction } from '../actions/menuAction';
-import * as classNames from 'classnames';
import { transportPCEUrl } from '../app';
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
const drawerWidth = 240;
@@ -141,16 +143,15 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
window.dispatchEvent(new Event('menu-resized'));
}, [isOpen])
- let menuItems = state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => {
- const reg = state.framework.applicationRegistraion[key];
- const icon = !reg.icon ? null :( typeof reg.icon === 'string' ? <img height={22} src={reg.icon} /> : <FontAwesomeIcon icon={reg.icon} /> )
+ let menuItems = state.framework.applicationRegistration && Object.keys(state.framework.applicationRegistration).map(key => {
+ const reg = state.framework.applicationRegistration[key];
return reg && (
<ListItemLink
key={reg.name}
to={reg.path || `/${reg.name}`}
primary={reg.menuEntry || reg.name}
secondary={reg.subMenuEntry}
- icon={icon} />
+ icon={reg.icon || null} />
) || null;
}) || null;
@@ -160,7 +161,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
key={"transportPCE"}
to={transportUrl}
primary={"TransportPCE"}
- icon={<FontAwesomeIcon icon={faProjectDiagram} />}
+ icon={faProjectDiagram}
external />;
const linkFound = menuItems.find(obj => obj.key === "linkCalculation");
@@ -191,17 +192,17 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
<div className={classes.toolbar} />
{ /* https://fiffty.github.io/react-treeview-mui/ */}
<List className={classes.menu} component="nav">
- <ListItemLink exact to="/" primary="Home" icon={<FontAwesomeIcon icon={faHome} />} />
+ <ListItemLink exact to="/" primary="Home" icon={homeIcon} />
<Divider />
{
menuItems
}
<Divider />
- <ListItemLink to="/about" primary="About" icon={<FontAwesomeIcon icon={faAddressBook} />} />
+ <ListItemLink to="/about" primary="About" icon={aboutIcon} />
{(false && process.env.NODE_ENV === "development")
? <>
<Divider />
- <ListItemLink to="/test" primary="Test" icon={<FontAwesomeIcon icon={faHome} />} />
+ <ListItemLink to="/test" primary="Test" icon={settingsIcon} />
</>
: null
}
diff --git a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx
index d055b8a87..aa22f17f4 100644
--- a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx
+++ b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx
@@ -15,9 +15,9 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React from 'react';
-import connect, { Connect } from '../../flux/connect';
+import { connect, Connect } from '../../flux/connect';
import { SetTitleAction } from '../../actions/titleActions';
import { AddErrorInfoAction } from '../../actions/errorActions';
diff --git a/sdnr/wt/odlux/framework/src/components/settings/general.tsx b/sdnr/wt/odlux/framework/src/components/settings/general.tsx
index 90f15c1d2..ffd516ba1 100644
--- a/sdnr/wt/odlux/framework/src/components/settings/general.tsx
+++ b/sdnr/wt/odlux/framework/src/components/settings/general.tsx
@@ -16,11 +16,11 @@
* ============LICENSE_END==========================================================================
*/
+import React from 'react';
import { Button, FormControlLabel, Switch, Typography } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import { SettingsComponentProps } from '../../models/settings';
-import * as React from 'react';
-import connect, { Connect, IDispatcher } from '../../flux/connect';
+import { connect, Connect, IDispatcher } from '../../flux/connect';
import { IApplicationStoreState } from '../../store/applicationStore';
import { getGeneralSettingsAction, SetGeneralSettingsAction, updateGeneralSettingsAction } from '../../actions/settingsAction';
import { sendMessage, SettingsMessage } from '../../services/broadcastService';
diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
index 19d3bdf74..40c0fc736 100644
--- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx
+++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
@@ -15,7 +15,7 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { Theme } from '@mui/material/styles';
@@ -27,9 +27,6 @@ import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
-import Block from '@mui/icons-material/Block';
-import Adjust from '@mui/icons-material/Adjust';
-import MenuIcon from '@mui/icons-material/Menu';
import AccountCircle from '@mui/icons-material/AccountCircle';
import MenuItem from '@mui/material/MenuItem';
import Menu from '@mui/material/Menu';
@@ -41,10 +38,12 @@ import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
import { logoutUser } from '../actions/authentication';
import { PushAction, ReplaceAction } from '../actions/navigationActions';
-import connect, { Connect, IDispatcher } from '../flux/connect';
-import Logo from './logo';
+import { connect, Connect, IDispatcher } from '../flux/connect';
import { MenuAction, MenuClosedByUser } from '../actions/menuAction';
+import MenuIcon from './icons/menuIcon';
+import Logo from './logo';
+
const styles = (theme: Theme) => createStyles({
appBar: {
zIndex: theme.zIndex.drawer + 1,
@@ -58,7 +57,8 @@ const styles = (theme: Theme) => createStyles({
},
icon: {
marginLeft: 16,
- marginRight: 8
+ marginRight: 8,
+ marginBottom: -2,
},
connected: {
color: "green"
@@ -112,10 +112,10 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
// add notificationInfo element before help
- if (state.framework.applicationRegistraion) {
+ if (state.framework.applicationRegistration) {
let isNotificationInfoAdded = false;
- Object.keys(state.framework.applicationRegistraion).map(key => {
- const reg = state.framework.applicationRegistraion[key];
+ Object.keys(state.framework.applicationRegistration).map(key => {
+ const reg = state.framework.applicationRegistration[key];
if (reg && reg.statusBarElement) {
if (key === "help") {
isNotificationInfoAdded = true;
@@ -132,7 +132,12 @@ class TitleBarComponent extends React.Component<TitleBarProps, { anchorEl: HTMLE
}
const stateIcon = state.framework.applicationState.icon;
- const icon = !stateIcon ? null :( typeof stateIcon === 'string' ? <img className={classes.icon} height={22} src={stateIcon} /> : <FontAwesomeIcon className={classes.icon} icon={stateIcon} /> )
+ const customIconHeight = 22;
+ const icon = !stateIcon
+ ? null
+ : (typeof stateIcon === 'string'
+ ? <img className={classes.icon} height={customIconHeight} src={stateIcon} />
+ : <FontAwesomeIcon className={classes.icon} icon={stateIcon} />)
return (
diff --git a/sdnr/wt/odlux/framework/src/flux/connect.ts b/sdnr/wt/odlux/framework/src/flux/connect.tsx
index f54e4e0f0..09d30dae7 100644
--- a/sdnr/wt/odlux/framework/src/flux/connect.ts
+++ b/sdnr/wt/odlux/framework/src/flux/connect.tsx
@@ -15,13 +15,14 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import * as PropTypes from 'prop-types';
+import React, { FC, useContext, createContext, useState, useEffect, useRef } from 'react';
-import { Dispatch } from '../flux/store';
+import { Dispatch } from './store';
import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore';
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.connect') || 0);
+
interface IApplicationStoreContext {
applicationStore: ApplicationStore;
}
@@ -38,12 +39,12 @@ interface IDispatchProps {
dispatch: Dispatch;
}
-type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
-
type ComponentDecoratorInfer<TMergedProps> = {
<TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<Omit<TProps & TMergedProps, keyof TMergedProps>>;
};
+const ApplicationStoreContext = createContext<IApplicationStoreContext | undefined>(undefined);
+
export type Connect<TMapProps extends ((...args: any) => any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> =
(TMapProps extends ((...args: any) => any) ? ReturnType<TMapProps> : IApplicationStoreProps) &
(TMapDispatch extends ((...args: any) => any) ? ReturnType<TMapDispatch> : IDispatchProps);
@@ -75,9 +76,7 @@ export function connect<TProps, TStateProps, TDispatchProps>(
const injectApplicationStore = (WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>): React.ComponentType<TProps> => {
class StoreAdapter extends React.Component<TProps, {}> {
- public static contextTypes = { ...WrappedComponent.contextTypes, applicationStore: PropTypes.object.isRequired };
- context: IApplicationStoreContext;
-
+
render(): JSX.Element {
if (isWrappedComponentIsVersion1(WrappedComponent)) {
@@ -112,7 +111,7 @@ export function connect<TProps, TStateProps, TDispatchProps>(
this.forceUpdate();
}
}
-
+ StoreAdapter.contextType = ApplicationStoreContext;
return StoreAdapter;
}
@@ -138,24 +137,77 @@ export function connect<TProps, TStateProps, TDispatchProps>(
}
}
-interface ApplicationStoreProviderProps extends React.Props<ApplicationStoreProvider> {
+type ApplicationStoreProviderProps = {
applicationStore: ApplicationStore;
}
-export class ApplicationStoreProvider extends React.Component<ApplicationStoreProviderProps>
- implements /* React.ComponentLifecycle<ApplicationStoreProviderProps, any>, */ React.ChildContextProvider<IApplicationStoreContext> {
+export const ApplicationStoreProvider: FC<ApplicationStoreProviderProps> = (props) => {
+ const { applicationStore, children } = props;
- public static childContextTypes = { applicationStore: PropTypes.object.isRequired };
+ return (
+ <ApplicationStoreContext.Provider value={{ applicationStore }}>
+ {children}
+ </ApplicationStoreContext.Provider>
+ );
+};
- getChildContext(): IApplicationStoreContext {
- return {
- applicationStore: this.props.applicationStore
- };
+export const useApplicationStore = (): ApplicationStore => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
}
+ return context.applicationStore
+};
- render(): JSX.Element {
- return React.Children.only(this.props.children) as any; //type error, fix when possible
+export const useSelectApplicationState = <TProp extends unknown >( selector: (state: IApplicationStoreState) => TProp, eqFunc = (a: TProp, b: TProp) => a === b ): TProp => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
}
-}
+
+ const [propState, setPropState] = useState<TProp>(selector(context.applicationStore.state));
+
+ const selectorRef = useRef(selector);
+ selectorRef.current = selector;
+
+ const propStateRef = useRef({propState});
+ propStateRef.current.propState = propState;
+
+ useEffect(() => {
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+
+ const changedHandler = () => {
+ const newState = selectorRef.current(context.applicationStore.state);
+ if (!eqFunc(newState, propStateRef.current.propState)) {
+ setPropState(newState);
+ }
+ };
+
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: adding handler", changedHandler);
+ }
+
+ context.applicationStore.changed.addHandler(changedHandler);
+
+ return () => {
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: removing handler", changedHandler);
+ }
-export default connect; \ No newline at end of file
+ context.applicationStore.changed.removeHandler(changedHandler);
+ }
+ }, [context]);
+
+ return propState;
+
+};
+
+export const useApplicationDispatch = (): Dispatch => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+ return context.applicationStore.dispatch;
+};
diff --git a/sdnr/wt/odlux/framework/src/flux/store.ts b/sdnr/wt/odlux/framework/src/flux/store.ts
index dd80ce7ba..347d295e0 100644
--- a/sdnr/wt/odlux/framework/src/flux/store.ts
+++ b/sdnr/wt/odlux/framework/src/flux/store.ts
@@ -20,6 +20,8 @@ import { Event } from "../common/event"
import { Action } from './action';
import { IActionHandler } from './action';
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.store') || 0);
+
export interface Dispatch {
<TAction extends Action>(action: TAction): TAction;
}
@@ -28,8 +30,8 @@ export interface Enhancer<TStoreState> {
(store: Store<TStoreState>): Dispatch;
}
-class InitialisationAction extends Action { };
-const initialisationAction = new InitialisationAction();
+class InitializationAction extends Action { };
+const initializationAction = new InitializationAction();
export class Store<TStoreState> {
@@ -43,19 +45,22 @@ export class Store<TStoreState> {
this._isDispatching = false;
- this.changed = new Event<void>(); // sollten wir hier eventuell sogar den state mit übergeben ?
+ this.changed = new Event<void>();
this._actionHandler = actionHandler;
this._state = initialState as TStoreState;
if (enhancer) this._dispatch = enhancer(this);
- this._dispatch(initialisationAction);
+ this._dispatch(initializationAction);
}
public changed: Event<void>;
private _dispatch: Dispatch = <TAction extends Action>(payload: TAction): TAction => {
+ if (LogLevel > 2) {
+ console.log('Store::Dispatch - ', payload);
+ }
if (payload == null || !(payload instanceof Action)) {
throw new Error(
'Actions must inherit from type Action. ' +
@@ -76,6 +81,9 @@ export class Store<TStoreState> {
}
if (this._state !== oldState) {
+ if (LogLevel > 3) {
+ console.log('Store::Dispatch - state has changed', this._state);
+ }
this.changed.invoke();
}
diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
index f98d77487..71b9e33d1 100644
--- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
+++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
@@ -23,7 +23,7 @@ import { AuthPolicy, User } from '../models/authentication';
import { onLogin, onLogout } from '../services/applicationApi';
import { startWebsocketSession, endWebsocketSession } from '../services/notificationService';
import { startUserSession, endUserSession } from '../services/userSessionService';
-import { getSettings } from '../services/settingsService';
+import { getUserdata } from '../services/userdataService';
export interface IAuthenticationState {
user?: User;
diff --git a/sdnr/wt/odlux/framework/src/middleware/logger.ts b/sdnr/wt/odlux/framework/src/middleware/logger.ts
index 47725e696..fb0874f3e 100644
--- a/sdnr/wt/odlux/framework/src/middleware/logger.ts
+++ b/sdnr/wt/odlux/framework/src/middleware/logger.ts
@@ -18,16 +18,17 @@
import { Dispatch } from '../flux/store';
import { MiddlewareApi } from '../store/applicationStore';
+const LogLevel = +(localStorage.getItem('log.odlux.framework.middleware.logger') || 0);
function createLoggerMiddleware() {
return function logger({ getState }: MiddlewareApi) {
return (next: Dispatch): Dispatch => action => {
- console.log('will dispatch', action);
+ if (LogLevel > 2) console.log('will dispatch', action);
const returnValue = next(action);
- console.log('state after dispatch', getState());
+ if (LogLevel > 2) console.log('state after dispatch', getState());
return returnValue;
};
- }
+ };
}
export const logger = createLoggerMiddleware();
diff --git a/sdnr/wt/odlux/framework/src/middleware/navigation.ts b/sdnr/wt/odlux/framework/src/middleware/navigation.ts
index 5f3eed55d..44035fec3 100644
--- a/sdnr/wt/odlux/framework/src/middleware/navigation.ts
+++ b/sdnr/wt/odlux/framework/src/middleware/navigation.ts
@@ -55,7 +55,7 @@ const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Di
// ensure user is logged in and token is valid
if (action.pathname.startsWith("/oauth") && (action.search.startsWith("?token="))){
const ind = action.search.lastIndexOf("token=");
- const tokenStr = ind > -1 ? action.search.substr(ind+6) : null;
+ const tokenStr = ind > -1 ? action.search.substring(ind+6) : null;
const token = tokenStr && jwt.decode(tokenStr);
if (tokenStr && token) {
// @ts-ignore
diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts
index 36523f9eb..8246ee8fa 100644
--- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts
+++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts
@@ -15,13 +15,10 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import { GeneralSettings } from '../models/settings';
-import { setGeneralSettingsAction, SetGeneralSettingsAction } from '../actions/settingsAction';
+
import { Event } from '../common/event';
import { ApplicationStore } from '../store/applicationStore';
import { AuthMessage, getBroadcastChannel, sendMessage } from './broadcastService';
-import { endWebsocketSession } from './notificationService';
-import { getSettings } from './settingsService';
let resolveApplicationStoreInitialized: (store: ApplicationStore) => void;
let applicationStore: ApplicationStore | null = null;
diff --git a/sdnr/wt/odlux/framework/src/services/applicationManager.ts b/sdnr/wt/odlux/framework/src/services/applicationManager.ts
index 14e3ed0b2..bd620a020 100644
--- a/sdnr/wt/odlux/framework/src/services/applicationManager.ts
+++ b/sdnr/wt/odlux/framework/src/services/applicationManager.ts
@@ -23,7 +23,7 @@ import { applicationApi } from './applicationApi';
/** Represents registry to manage all applications. */
class ApplicationManager {
- /** Stores all registerd applications. */
+ /** Stores all registered applications. */
private _applications: { [key: string]: ApplicationInfo };
/** Initializes a new instance of this class. */
@@ -32,7 +32,7 @@ class ApplicationManager {
this.changed = new Event<void>();
}
- /** The chaged event will fire if the registration has changed. */
+ /** The changed event will fire if the registration has changed. */
public changed: Event<void>;
/** Registers a new application. */
diff --git a/sdnr/wt/odlux/framework/src/services/broadcastService.ts b/sdnr/wt/odlux/framework/src/services/broadcastService.ts
index f2c3ebc57..40968e54f 100644
--- a/sdnr/wt/odlux/framework/src/services/broadcastService.ts
+++ b/sdnr/wt/odlux/framework/src/services/broadcastService.ts
@@ -22,89 +22,95 @@ import { ReplaceAction } from "../actions/navigationActions";
import { User } from "../models/authentication";
import { ApplicationStore } from "../store/applicationStore";
-type Broadcaster = {channel: BroadcastChannel, key: String};
+type Broadcaster = {
+ channel: BroadcastChannel;
+ key: String;
+};
type AuthTypes = 'login' | 'logout';
-export type AuthMessage={key: AuthTypes, data: any};
+export type AuthMessage = {
+ key: AuthTypes;
+ data: any;
+};
type SettingsType = 'general';
-export type SettingsMessage={key: SettingsType, enableNotifications: boolean, user: string};
+export type SettingsMessage = {
+ key: SettingsType;
+ enableNotifications: boolean;
+ user: string;
+};
-let channels: Broadcaster[] = [];
-let store : ApplicationStore | null = null;
+const channels: Broadcaster[] = [];
+let store: ApplicationStore | null = null;
export const saveChannel = (channel: BroadcastChannel, channelName: string) => {
- channels.push({channel: channel, key: channelName});
-}
-
-export const startBroadcastChannel = (applicationStore: ApplicationStore)=>{
- store=applicationStore;
-
- //might decide to use one general broadcast channel with more keys in the future
- createAuthBroadcastChannel();
- createSettingsBroadcastChannel();
-}
-
-const createSettingsBroadcastChannel = () =>{
-
- const name = "odlux_settings";
- const bc: BroadcastChannel = new BroadcastChannel(name);
- channels.push({ channel: bc, key: name });
-
- bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => {
- console.log(eventMessage)
-
- if (eventMessage.data.key === 'general') {
-
- if (store?.state.framework.authenticationState.user) {
- const data = eventMessage.data;
- if(store.state.framework.authenticationState.user.user === data.user){
- store?.dispatch(setGeneralSettingsAction(data.enableNotifications));
- }
- }
- }
- }
+ channels.push({ channel: channel, key: channelName });
+};
-}
+export const startBroadcastChannel = (applicationStore: ApplicationStore) => {
+ store = applicationStore;
-const createAuthBroadcastChannel = () => {
- const name = "odlux_auth";
- const bc: BroadcastChannel = new BroadcastChannel(name);
- channels.push({ channel: bc, key: name });
-
- bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => {
- console.log(eventMessage)
-
- if (eventMessage.data.key === 'login') {
- if (!store?.state.framework.authenticationState.user) {
- const initialToken = localStorage.getItem("userToken");
- if (initialToken) {
- store?.dispatch(loginUserAction(User.fromString(initialToken)));
- store?.dispatch(new ReplaceAction("/"));
- }
- }
- }
- else if (eventMessage.data.key === 'logout') {
+ //might decide to use one general broadcast channel with more keys in the future
+ createAuthBroadcastChannel();
+ createSettingsBroadcastChannel();
+};
- if (store?.state.framework.authenticationState.user) {
- store?.dispatch(logoutUser());
- store?.dispatch(new ReplaceAction("/login"));
- }
- }
- }
-}
+const createSettingsBroadcastChannel = () => {
-export const getBroadcastChannel = (channelName: string) =>{
- const foundChannel = channels.find(s =>s.key===channelName);
- return foundChannel?.channel;
-}
+ const name = "odlux_settings";
+ const bc: BroadcastChannel = new BroadcastChannel(name);
+ channels.push({ channel: bc, key: name });
+ bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => {
+ console.log(eventMessage);
-export const sendMessage = (data: any, channel: string) =>{
+ if (eventMessage.data.key === 'general') {
- const foundChannel = channels.find(s =>s.key===channel);
- if(foundChannel){
- foundChannel.channel.postMessage(data);
- }
+ if (store?.state.framework.authenticationState.user) {
+ const data = eventMessage.data;
+ if (store.state.framework.authenticationState.user.user === data.user) {
+ store?.dispatch(setGeneralSettingsAction(data.enableNotifications));
+ }
+ }
+ }
+ }
+};
+
+const createAuthBroadcastChannel = () => {
+ const name = "odlux_auth";
+ const bc: BroadcastChannel = new BroadcastChannel(name);
+ channels.push({ channel: bc, key: name });
+
+ bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => {
+ console.log(eventMessage)
+
+ if (eventMessage.data.key === 'login') {
+ if (!store?.state.framework.authenticationState.user) {
+ const initialToken = localStorage.getItem("userToken");
+ if (initialToken) {
+ store?.dispatch(loginUserAction(User.fromString(initialToken)));
+ store?.dispatch(new ReplaceAction("/"));
+ }
+ }
+ }
+ else if (eventMessage.data.key === 'logout') {
+ if (store?.state.framework.authenticationState.user) {
+ store?.dispatch(logoutUser());
+ store?.dispatch(new ReplaceAction("/login"));
+ }
}
+ }
+};
+
+export const getBroadcastChannel = (channelName: string) => {
+ const foundChannel = channels.find(s => s.key === channelName);
+ return foundChannel?.channel;
+};
+
+export const sendMessage = (data: any, channel: string) => {
+ const foundChannel = channels.find(s => s.key === channel);
+ if (foundChannel) {
+ foundChannel.channel.postMessage(data);
+ }
+};
diff --git a/sdnr/wt/odlux/framework/src/services/index.ts b/sdnr/wt/odlux/framework/src/services/index.ts
index 19b451345..85f0708a6 100644
--- a/sdnr/wt/odlux/framework/src/services/index.ts
+++ b/sdnr/wt/odlux/framework/src/services/index.ts
@@ -18,5 +18,5 @@
export { applicationManager } from './applicationManager';
export { subscribe, unsubscribe } from './notificationService';
export { requestRest } from './restService';
-export { putSettings, getSettings} from './settingsService';
+export { saveUserdata, getUserdata } from './userdataService';
diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts
index d727e4c9e..a296c52a4 100644
--- a/sdnr/wt/odlux/framework/src/services/restService.ts
+++ b/sdnr/wt/odlux/framework/src/services/restService.ts
@@ -16,18 +16,13 @@
* ============LICENSE_END==========================================================================
*/
+import { ReplaceAction } from '../actions/navigationActions';
+import { AddErrorInfoAction } from '../actions/errorActions';
-import { ApplicationStore } from "../store/applicationStore";
-import { ReplaceAction } from "../actions/navigationActions";
-import { AddErrorInfoAction } from "../actions/errorActions";
+import { storeService } from './storeService';
const baseUri = `${ window.location.origin }`;
const absUrlPattern = /^https?:\/\//;
-let applicationStore: ApplicationStore | null = null;
-
-export const startRestService = (store: ApplicationStore) => {
- applicationStore = store;
-};
export const formEncode = (params: { [key: string]: string | number }) => Object.keys(params).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString());
@@ -46,9 +41,9 @@ export const getAccessPolicyByUrl = (url: string) => {
DELETE: false,
};
- if (!applicationStore) return result;
+ if (!storeService.applicationStore) return result;
- const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies }}} } = applicationStore!;
+ const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies } } } } = storeService.applicationStore!;
result.GET = true;
result.POST = true;
@@ -71,7 +66,7 @@ export const getAccessPolicyByUrl = (url: string) => {
return result;
-}
+};
/** Sends a rest request to the given path.
* @returns The data, or null it there was any error
@@ -87,8 +82,8 @@ export async function requestRest<TData>(path: string = '', init: RequestInit =
/** Sends a rest request to the given path and reports the server state.
* @returns An object with the server state, a message and the data or undefined in case of a json parse error.
*/
-export async function requestRestExt<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number, message?: string, data: TData | null | undefined }> {
- const result: { status: number, message?: string, data: TData | null } = {
+export async function requestRestExt<TData>(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number; message?: string; data: TData | null | undefined }> {
+ const result: { status: number; message?: string; data: TData | null } = {
status: -1,
data: null,
};
@@ -100,60 +95,59 @@ export async function requestRestExt<TData>(path: string = '', init: RequestInit
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
- ...init.headers
- }
+ ...init.headers,
+ },
};
- if (!isAbsUrl && authenticate && applicationStore) {
- const { state: { framework: { authenticationState: { user } } } } = applicationStore;
+ if (!isAbsUrl && authenticate && storeService.applicationStore) {
+ const { state: { framework: { authenticationState: { user } } } } = storeService.applicationStore;
// do not request if the user is not valid
if (!user || !user.isValid) {
return {
...result,
- message: "User is not valid or not logged in."
+ message: 'User is not valid or not logged in.',
};
}
(init.headers = {
...init.headers,
- 'Authorization': `${user.tokenType} ${user.token}`
+ 'Authorization': `${user.tokenType} ${user.token}`,
//'Authorization': 'Basic YWRtaW46YWRtaW4='
});
}
const fetchResult = await fetch(uri, init);
- if(fetchResult.status === 403){
- applicationStore && applicationStore.dispatch(new AddErrorInfoAction({title: "Forbidden", message:"Status: [403], access denied."}));
+ if (fetchResult.status === 403) {
+ storeService.applicationStore && storeService.applicationStore.dispatch(new AddErrorInfoAction({ title: 'Forbidden', message:'Status: [403], access denied.' }));
return {
...result,
status: 403,
- message: "Forbidden."
+ message: 'Forbidden.',
};
- }
- else if (fetchResult.status === 401) {
- applicationStore && applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${applicationStore.state.framework.navigationState.pathname}`));
+ } else if (fetchResult.status === 401) {
+ storeService.applicationStore && storeService.applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${storeService.applicationStore.state.framework.navigationState.pathname}`));
return {
...result,
status: 401,
- message: "Authentication requested by server."
+ message: 'Authentication requested by server.',
};
}
- const contentType = fetchResult.headers.get("Content-Type") || fetchResult.headers.get("content-type");
- const isJson = contentType && (contentType.toLowerCase().startsWith("application/json") || contentType.toLowerCase().startsWith("application/yang-data+json"));
+ const contentType = fetchResult.headers.get('Content-Type') || fetchResult.headers.get('content-type');
+ const isJson = contentType && (contentType.toLowerCase().startsWith('application/json') || contentType.toLowerCase().startsWith('application/yang-data+json'));
try {
const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData;
return {
...result,
status: fetchResult.status,
message: fetchResult.statusText,
- data: data
+ data: data,
};
} catch (error) {
return {
...result,
status: fetchResult.status,
message: error && error.message || String(error),
- data: undefined
+ data: undefined,
};
}
} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/services/storeService.ts b/sdnr/wt/odlux/framework/src/services/storeService.ts
new file mode 100644
index 000000000..cbb5987de
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/services/storeService.ts
@@ -0,0 +1,11 @@
+import { ApplicationStore } from "../store/applicationStore";
+
+let applicationStore: ApplicationStore | null = null;
+
+export const startSoreService = (store: ApplicationStore) => {
+ applicationStore = store;
+};
+
+export const storeService = {
+ get applicationStore() { return applicationStore; },
+ }; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/services/settingsService.ts b/sdnr/wt/odlux/framework/src/services/userdataService.ts
index 6633a794d..5c9b576c3 100644
--- a/sdnr/wt/odlux/framework/src/services/settingsService.ts
+++ b/sdnr/wt/odlux/framework/src/services/userdataService.ts
@@ -22,7 +22,7 @@ import { requestRest } from "./restService";
const settingsPath ="/userdata";
- export function getSettings<TData>(partialPath?: string){
+ export function getUserdata<TData>(partialPath?: string){
let path = settingsPath;
if(partialPath){
path+=partialPath
@@ -32,7 +32,7 @@ import { requestRest } from "./restService";
return result;
}
- export function putSettings<TData>(partialPath: string, data: string){
+ export function saveUserdata<TData>(partialPath: string, data: string){
const result = requestRest<TData>(settingsPath+partialPath, {method: "PUT", body: data})
return result;
diff --git a/sdnr/wt/odlux/framework/src/store/applicationStore.ts b/sdnr/wt/odlux/framework/src/store/applicationStore.ts
index a4545eff9..cbe8c20da 100644
--- a/sdnr/wt/odlux/framework/src/store/applicationStore.ts
+++ b/sdnr/wt/odlux/framework/src/store/applicationStore.ts
@@ -37,7 +37,7 @@ import { updatePolicies } from '../middleware/policies';
export type MiddlewareApi = MiddlewareArg<IApplicationStoreState>;
export interface IFrameworkStoreState {
- applicationRegistraion: IApplicationRegistration;
+ applicationRegistration: IApplicationRegistration;
applicationState: IApplicationState;
authenticationState: IAuthenticationState;
navigationState: INavigationState;
@@ -48,7 +48,7 @@ export interface IApplicationStoreState {
}
const frameworkHandlers = combineActionHandler({
- applicationRegistraion: applicationRegistryHandler,
+ applicationRegistration: applicationRegistryHandler,
applicationState: applicationStateHandler,
authenticationState: authenticationStateHandler,
navigationState: navigationStateHandler
@@ -62,7 +62,7 @@ export const applicationStoreCreator = (): ApplicationStore => {
const actionHandlers = Object.keys(applicationService.applications).reduce((acc, cur) => {
const reg = applicationService.applications[cur];
reg && typeof reg.rootActionHandler === 'function' && (acc[cur] = reg.rootActionHandler);
- reg && +(reg.middlewares || 0) && middlewares.push(...(reg.middlewares as Middleware<IApplicationStoreState>[]));
+ reg && reg.middlewares && Array.isArray(reg.middlewares) && middlewares.push(...(reg.middlewares as Middleware<IApplicationStoreState>[]));
return acc;
}, { framework: frameworkHandlers } as any);
diff --git a/sdnr/wt/odlux/framework/src/utilities/logLevel.ts b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts
new file mode 100644
index 000000000..a198d98a9
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts
@@ -0,0 +1,8 @@
+export enum LogLevel {
+ Always = 0,
+ Error = 1,
+ Warning = 2,
+ Info = 3,
+ Debug = 4,
+ Trace = 5,
+}
diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx
index ac219708d..937e74f33 100644
--- a/sdnr/wt/odlux/framework/src/views/about.tsx
+++ b/sdnr/wt/odlux/framework/src/views/about.tsx
@@ -15,22 +15,18 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React, { FC, useEffect, useState } from 'react';
import * as marked from 'marked';
import * as hljs from 'highlight.js';
import { requestRestExt } from '../services/restService';
import { Button, Typography } from '@mui/material';
+
const defaultRenderer = new marked.Renderer();
defaultRenderer.link = (href, title, text) => (
`<a target="_blank" rel="noopener noreferrer" href="${href}" title="${title}">${text}</a>`
);
-interface AboutState {
- content: string | null;
- isCopiedSuccessfully: boolean;
- isContentLoadedSucessfully: boolean;
-}
-type odluxVersion= {version:string,build:string, framework: string,
+type OdluxVersion= {version:string,build:string, framework: string,
applications:{
configurationApp: string,
connectApp: string,
@@ -38,25 +34,27 @@ type odluxVersion= {version:string,build:string, framework: string,
faultApp: string,
helpApp: string,
inventoryApp: string,
+ linkCalculationApp: string,
maintenanceApp: string,
mediatorApp: string,
+ networkMapApp: string,
permanceHistoryApp: string
}};
-type topologyVersion = {version: string, buildTimestamp: string};
-
-class AboutComponent extends React.Component<any, AboutState> {
- textarea: React.RefObject<HTMLTextAreaElement>;
+type TopologyVersion = {version: string, buildTimestamp: string};
+const AboutComponent: FC = (props) => {
+
+ const textareaRef = React.createRef<HTMLTextAreaElement>();
+ const [content, setContent] = useState<string | null>(null);
+ const [isCopiedSuccessfully, setCopySuccess] = useState(false);
+ const [isContetLoaded, setContentLoaded] = useState(false);
- constructor(props: any) {
- super(props);
- this.state = { content: null, isCopiedSuccessfully:false, isContentLoadedSucessfully: false }
- this.textarea = React.createRef();
- this.loadAboutContent();
- }
+ useEffect(()=>{
+ loadAboutContent();
+ },[]);
- private getMarkOdluxVersionMarkdownTable(data:odluxVersion|null|undefined):string{
+ const getMarkOdluxVersionMarkdownTable = (data:OdluxVersion|null|undefined):string => {
if(!data) {
return "";
}else{
@@ -72,6 +70,8 @@ class AboutComponent extends React.Component<any, AboutState> {
`| InventoryApp | ${data.applications.inventoryApp}|\n `+
`| EventLogApp | ${data.applications.eventLogApp}|\n `+
`| MediatorApp | ${data.applications.mediatorApp}|\n `+
+ `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+
+ `| LinkCalculatorApp | ${data.applications.linkCalculationApp}|\n `+
`| HelpApp | ${data.applications.helpApp}|\n `;
}
@@ -80,7 +80,7 @@ class AboutComponent extends React.Component<any, AboutState> {
}
}
- private getTopologyVersionMarkdownTable(data: topologyVersion|null|undefined){
+ const getTopologyVersionMarkdownTable = (data: TopologyVersion|null|undefined) => {
if(!data){
return "No version";
}
@@ -92,7 +92,7 @@ class AboutComponent extends React.Component<any, AboutState> {
}
}
- private loadAboutContent(): void {
+ const loadAboutContent = (): void => {
const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1);
const init = {
'method': 'GET',
@@ -102,7 +102,7 @@ class AboutComponent extends React.Component<any, AboutState> {
}
};
const p1 = requestRestExt<string>('/about',init);
- const p2 = requestRestExt<odluxVersion>(`${baseUri}version.json`);
+ const p2 = requestRestExt<OdluxVersion>(`${baseUri}version.json`);
const p3 = requestRestExt<any>(`/topology/info/version`);
Promise.all([p1,p2, p3]).then((responses) => {
@@ -110,31 +110,30 @@ class AboutComponent extends React.Component<any, AboutState> {
const response2 = responses[1];
const response3 = responses[2];
const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error";
- const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error");
- const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? this.getTopologyVersionMarkdownTable(response3.data): `Topology API not available`);
+ const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error");
+ const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? getTopologyVersionMarkdownTable(response3.data): `Topology API not available`);
const loadedSucessfully = response.status == 200 ? true : false;
- this.setState({ content: (content + content2 + content3 ) || null, isContentLoadedSucessfully: loadedSucessfully });
+ setContent((content + content2 + content3 ) || null);
+ setContentLoaded(loadedSucessfully);
}).catch((error) => {
- this.setState({ content: error })
- })
+ setContent(error);
+ });
}
- copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
+ const copyToClipboard = (e: React.MouseEvent<HTMLButtonElement>) =>{
e.preventDefault();
- if(this.textarea.current!==null){
- this.textarea.current.select();
+ if(textareaRef.current!==null){
+ textareaRef.current.select();
document.execCommand('copy');
if(e.currentTarget != null){ // refocus on button, otherwhise the textarea would be focused
e.currentTarget.focus();
}
- this.setState({isCopiedSuccessfully: true});
- window.setTimeout(()=>{this.setState({isCopiedSuccessfully: false});},2000);
+ setCopySuccess(true);
+ window.setTimeout(()=>{ setCopySuccess(false);},2000);
}
}
- render() {
-
const markedOptions: marked.MarkedOptions = {
gfm: true,
breaks: false,
@@ -157,17 +156,17 @@ class AboutComponent extends React.Component<any, AboutState> {
const style: React.CSSProperties = {};
const containerStyle = { overflow: "auto", paddingRight: "20px" }
- const html = (marked(this.state.content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
+ const html = (marked(content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
return (
<div style={containerStyle}>
- { this.state.isContentLoadedSucessfully &&
+ { isContetLoaded &&
<div style={{float: "right", marginRight: "10px"}}>
- <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => this.copyToClipboard(e)}>
+ <Button aria-label="copy-version-information-button" color="inherit" variant="contained" onClick={e => copyToClipboard(e)}>
Copy to clipboard
</Button>
{
- this.state.isCopiedSuccessfully &&
+ isCopiedSuccessfully &&
<Typography variant="body1" style={{color: "green"}} align="center">
copied successfully
</Typography>
@@ -183,13 +182,12 @@ class AboutComponent extends React.Component<any, AboutState> {
<form>
<textarea
style={{opacity: ".01"}}
- ref={this.textarea}
- value={this.state.content || ''}
+ ref={textareaRef}
+ value={content || ''}
/>
</form>
</div>
);
- }
};
export const About = AboutComponent;
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx
index 4676f5ac2..4a93cf0ae 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -15,15 +15,11 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
+import React, { FC, memo } from 'react';
import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
import { Theme } from '@mui/material/styles';
-import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
-import createStyles from '@mui/styles/createStyles';
-import { faHome, faAddressBook, faSignInAlt, faCog } from '@fortawesome/free-solid-svg-icons'
-
+import { makeStyles } from '@mui/styles';
import { SnackbarProvider } from 'notistack';
import { ConfirmProvider } from 'material-ui-confirm';
@@ -42,8 +38,14 @@ import UserSettings from '../views/settings';
import applicationService from '../services/applicationManager';
+const aboutIcon = require('../assets/icons/About.svg');
+const homeIcon = require('../assets/icons/Home.svg');
+const loginIcon = require('../assets/icons/User.svg');
+const settingsIcon = require('../assets/icons/Tools.svg');
+
+const styles = makeStyles((theme: Theme) => {
-const styles = (theme: Theme) => createStyles({
+ return {
root: {
flexGrow: 1,
height: '100%',
@@ -61,74 +63,69 @@ const styles = (theme: Theme) => createStyles({
minWidth: 0, // So the Typography noWrap works
},
toolbar: theme.mixins.toolbar as any
+ };
});
+const FrameComponent: FC = memo(() => {
+ const registrations = applicationService.applications;
+ const classes = styles();
+ return (
+ <ConfirmProvider>
+ <SnackbarProvider maxSnack={3}>
+ <Router>
+ <div className={classes.root}>
+ <SnackDisplay />
+ <ErrorDisplay />
+ <TitleBar />
+ <Menu />
+ <main className={classes.content}>
+ {
+ <div className={classes.toolbar} /> //needed for margins, don't remove!
+ }
+ <Switch>
+ <Route exact path="/" component={() => (
+ <AppFrame title={"Home"} icon={homeIcon} >
+ <Home />
+ </AppFrame>
+ )} />
+ <Route path="/about" component={() => (
+ <AppFrame title={"About"} icon={aboutIcon} >
+ <About />
+ </AppFrame>
+ )} />
+ <Route path="/settings" component={() => (
+ <AppFrame title={"Settings"} icon={settingsIcon} >
+ <UserSettings />
+ </AppFrame>
+ )} />
+ {process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
+ <AppFrame title={"Test"} icon={settingsIcon} >
+ <Test />
+ </AppFrame>
+ )} /> : null}
+ <Route path="/login" component={() => (
+ <AppFrame title={"Login"} icon={loginIcon} >
+ <Login />
+ </AppFrame>
+ )} />
+ {Object.keys(registrations).map(p => {
+ const application = registrations[p];
+ return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
+ <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
+ <application.rootComponent />
+ </AppFrame>
+ )} />)
+ })}
+ <Redirect to="/" />
+ </Switch>
+ </main>
+ </div>
+ </Router>
+ </SnackbarProvider>
+ </ConfirmProvider>
+ );
+});
-type FrameProps = WithStyles<typeof styles>;
-
-class FrameComponent extends React.Component<FrameProps>{
-
- render() {
- const registrations = applicationService.applications;
- const { classes } = this.props;
- return (
- <ConfirmProvider>
- <SnackbarProvider maxSnack={3}>
- <Router>
- <div className={classes.root}>
- <SnackDisplay />
- <ErrorDisplay />
- <TitleBar />
- <Menu />
- <main className={classes.content}>
- {
- <div className={classes.toolbar} /> //needed for margins, don't remove!
- }
- <Switch>
- <Route exact path="/" component={() => (
- <AppFrame title={"Home"} icon={faHome} >
- <Home />
- </AppFrame>
- )} />
- <Route path="/about" component={() => (
- <AppFrame title={"About"} icon={faAddressBook} >
- <About />
- </AppFrame>
- )} />
- <Route path="/settings" component={() => (
- <AppFrame title={"Settings"} icon={faCog} >
- <UserSettings />
- </AppFrame>
- )} />
- {process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
- <AppFrame title={"Test"} icon={faAddressBook} >
- <Test />
- </AppFrame>
- )} /> : null}
- <Route path="/login" component={() => (
- <AppFrame title={"Login"} icon={faSignInAlt} >
- <Login />
- </AppFrame>
- )} />
- { Object.keys(registrations).map(p => {
- const application = registrations[p];
- return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
- <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
- <application.rootComponent />
- </AppFrame>
- )} />)
- })}
- <Redirect to="/" />
- </Switch>
- </main>
- </div>
- </Router>
- </SnackbarProvider>
- </ConfirmProvider>
- );
- }
-}
-
-export const Frame = withStyles(styles)(FrameComponent);
+export const Frame = FrameComponent;
export default Frame;
diff --git a/sdnr/wt/odlux/framework/src/views/home.tsx b/sdnr/wt/odlux/framework/src/views/home.tsx
index 92fd0b262..72c5059e1 100644
--- a/sdnr/wt/odlux/framework/src/views/home.tsx
+++ b/sdnr/wt/odlux/framework/src/views/home.tsx
@@ -16,50 +16,39 @@
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState} from 'react';
import applicationService from '../services/applicationManager';
-type props = Connect<typeof mapProps, typeof mapDispatch>;
-type SettingsEntry = { name: string, element: JSX.Element }
+type DashboardElement = { name: string, element: JSX.Element };
-
-const mapProps = (state: IApplicationStoreState) => ({
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
-});
-
-const DashboardView: React.FunctionComponent<props> = (props) => {
+const DashboardView: FC = (props) => {
const registrations = applicationService.applications;
- const [selectedIndex] = React.useState(0);
+ const [selectedIndex] = useState(0);
- let settingsArray: SettingsEntry[] = [];
+ let dashboardArray: DashboardElement[] = [];
- let settingsElements: (SettingsEntry)[] = Object.keys(registrations).map(p => {
+ let dashboardElements: (DashboardElement)[] = Object.keys(registrations).map(p => {
const application = registrations[p];
if (application.dashbaordElement) {
- const value: SettingsEntry = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
+ const value: DashboardElement = { name: application.menuEntry?.toString()!, element: <application.dashbaordElement /> };
return value;
} else {
return null;
}
- }).filter((x): x is SettingsEntry => x !== null);
-
+ }).filter((x): x is DashboardElement => x !== null);
- settingsArray.push(...settingsElements);
+ dashboardArray.push(...dashboardElements);
return <div>
<div>
<div>
{
- settingsArray[selectedIndex]?.element
+ dashboardArray[selectedIndex]?.element
}
</div>
</div>
@@ -67,4 +56,4 @@ const DashboardView: React.FunctionComponent<props> = (props) => {
}
-export default connect(mapProps, mapDispatch)(DashboardView);
+export default DashboardView; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx
index e037edf82..46c0872bc 100644
--- a/sdnr/wt/odlux/framework/src/views/login.tsx
+++ b/sdnr/wt/odlux/framework/src/views/login.tsx
@@ -15,39 +15,34 @@
* the License.
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { withRouter, RouteComponentProps } from 'react-router-dom';
+import React, { FC, useEffect, useState } from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
import Alert from '@mui/material/Alert';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import FormControl from '@mui/material/FormControl';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Checkbox from '@mui/material/Checkbox';
import Input from '@mui/material/Input';
import InputLabel from '@mui/material/InputLabel';
-import LockIcon from '@mui/icons-material/LockOutlined';
import Paper from '@mui/material/Paper';
-import Typography from '@mui/material/Typography';
import { Theme } from '@mui/material/styles';
+import Typography from '@mui/material/Typography';
-import { WithStyles } from '@mui/styles';
-import withStyles from '@mui/styles/withStyles';
-import createStyles from '@mui/styles/createStyles';
+import { makeStyles } from '@mui/styles';
-import connect, { Connect, IDispatcher } from '../flux/connect';
+import { useApplicationDispatch, useSelectApplicationState } from '../flux/connect';
import authenticationService from '../services/authenticationService';
-import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
import { loginUserAction, UpdatePolicies } from '../actions/authentication';
+import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
-import { IApplicationStoreState } from '../store/applicationStore';
import { AuthPolicy, AuthToken, User } from '../models/authentication';
-import Menu from '@mui/material/Menu';
-import { MenuItem } from '@mui/material';
-const styles = (theme: Theme) => createStyles({
+const loginIcon = require('../assets/icons/User.svg');
+
+const styles = makeStyles((theme: Theme) =>{
+ return{
layout: {
width: 'auto',
display: 'block', // Fix IE11 issue.
@@ -91,204 +86,165 @@ const styles = (theme: Theme) => createStyles({
padding: '0 10px',
color: 'grey'
}
+};
});
-const mapProps = (state: IApplicationStoreState) => ({
- search: state.framework.navigationState.search,
- authentication: state.framework.applicationState.authentication,
- externalLoginProviders: state.framework.applicationState.externalLoginProviders ,
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
- updateExternalProviders: () => dispatcher.dispatch(updateExternalLoginProviderAsyncActionCreator()),
- updateAuthentication: (token: AuthToken | null) => {
- const user = token && new User(token) || undefined;
- dispatcher.dispatch(loginUserAction(user));
- },
- updatePolicies: (policies?: AuthPolicy[]) => {
- return dispatcher.dispatch(new UpdatePolicies(policies));
- },
-});
-
-type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
-
-interface ILoginState {
- externalProviderAnchor: HTMLElement | null;
- busy: boolean;
- username: string;
- password: string;
- scope: string;
- message: string;
- isServerReady: boolean;
- providers: {
- id: string;
- title: string;
- loginUrl: string;
- }[] | null;
-}
+type LoginProps = RouteComponentProps;
// todo: ggf. redirect to einbauen
-class LoginComponent extends React.Component<LoginProps, ILoginState> {
+const LoginComponent: FC<LoginProps> = (props) => {
- constructor(props: LoginProps) {
- super(props);
-
- this.state = {
- externalProviderAnchor: null,
- busy: false,
- username: '',
- password: '',
- scope: 'sdn',
- message: '',
- providers: null,
- isServerReady: false
- };
+ const search = useSelectApplicationState(state => state.framework.navigationState.search);
+ const authentication = useSelectApplicationState(state => state.framework.applicationState.authentication);
+ const externalLoginProviders = useSelectApplicationState(state => state.framework.applicationState.externalLoginProviders);
+
+ const dispatch = useApplicationDispatch();
+ const updateExternalProviders = () => dispatch(updateExternalLoginProviderAsyncActionCreator());
+ const updateAuthentication = (token: AuthToken | null) => {
+ const user = token && new User(token) || undefined;
+ dispatch(loginUserAction(user));
+ }
+ const updatePolicies = (policies?: AuthPolicy[]) => {
+ return dispatch(new UpdatePolicies(policies));
}
- async componentDidMount(){
- if (this.props.authentication === "oauth" && (this.props.externalLoginProviders == null || this.props.externalLoginProviders.length === 0)){
- this.props.updateExternalProviders();
+ const [isBusy, setBusy] = useState(false);
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [scope, setScope] = useState("sdn");
+ const [message, setMessage] = useState("");
+ const [isServerReady, setIsServerReady] = useState(false);
+
+ useEffect(()=>{
+ if (authentication === "oauth" && (externalLoginProviders == null || externalLoginProviders.length === 0)){
+ updateExternalProviders();
}
authenticationService.getServerReadyState().then(result =>{
- this.setState({isServerReady: result});
+ setIsServerReady(result);
})
+ },[]);
-
-
- }
-
- private setExternalProviderAnchor = (el: HTMLElement | null) => {
- this.setState({externalProviderAnchor: el })
- }
-
- render(): JSX.Element {
- const { classes } = this.props;
- const areProvidersAvailable = this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0;
- return (
- <>
- <CssBaseline />
- <main className={classes.layout}>
- <Paper className={classes.paper}>
- <Avatar className={classes.avatar}>
- <LockIcon />
- </Avatar>
- <Typography variant="caption">Sign in</Typography>
- <form className={classes.form}>
-
-
- {areProvidersAvailable &&
- <>
- {
- this.props.externalLoginProviders!.map((provider, index) => (
- <Button
- aria-controls="externalLogin"
- aria-label={"external-login-identity-provider-" + (index + 1)}
- aria-haspopup="true"
- fullWidth
- variant="contained"
- color="inherit"
- className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
- {provider.title}
- </Button>))
- }
-
- <div className={classes.lineContainer}>
- <span className={classes.thirdPartyDivider}>
- OR
- </span>
- </div>
- </>
- }
-
- <FormControl variant="standard" margin="normal" required fullWidth>
- <InputLabel htmlFor="username">Username</InputLabel>
- <Input id="username" name="username" autoComplete="username" autoFocus
- disabled={this.state.busy}
- value={this.state.username}
- onChange={event => { this.setState({ username: event.target.value }) }} />
- </FormControl>
- <FormControl variant="standard" margin="normal" required fullWidth>
- <InputLabel htmlFor="password">Password</InputLabel>
- <Input
- name="password"
- type="password"
- id="password"
- autoComplete="current-password"
- disabled={this.state.busy}
- value={this.state.password}
- onChange={event => { this.setState({ password: event.target.value }) }}
- />
- </FormControl>
- <FormControl variant="standard" margin="normal" required fullWidth>
- <InputLabel htmlFor="password">Domain</InputLabel>
- <Input
- name="scope"
- type="scope"
- id="scope"
- disabled={this.state.busy}
- value={this.state.scope}
- onChange={event => { this.setState({ scope: event.target.value }) }}
- />
- </FormControl>
- <Button
- aria-label="login-button"
- type="submit"
- fullWidth
- variant="contained"
- color="inherit"
- disabled={this.state.busy}
- className={classes.submit}
- onClick={this.onSignIn}
- >
- Sign in
- </Button>
-
- </form>
- {this.state.message && <Alert severity="error">{this.state.message}</Alert>}
- </Paper>
- </main>
- </>
- );
- }
-
- private onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
+ const onSignIn = async (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
+
+ setBusy(true);
- this.setState({ busy: true });
-
- const token = this.props.authentication === "oauth"
- ? await authenticationService.authenticateUserOAuth(this.state.username, this.state.password, this.state.scope)
- : await authenticationService.authenticateUserBasicAuth(this.state.username, this.state.password, this.state.scope);
+ const token = authentication === "oauth"
+ ? await authenticationService.authenticateUserOAuth(username, password, scope)
+ : await authenticationService.authenticateUserBasicAuth(username, password, scope);
- this.props.updateAuthentication(token);
- this.setState({ busy: false });
+ updateAuthentication(token);
+ setBusy(false);
if (token) {
- const query = this.props.search && this.props.search.replace(/^\?/, "").split('&').map(e => e.split("="));
+ const query = search && search.replace(/^\?/, "").split('&').map(e => e.split("="));
const returnTo = query && query.find(e => e[0] === "returnTo");
- this.props.history.replace(returnTo && returnTo[1] || "/");
+ props.history.replace(returnTo && returnTo[1] || "/");
}
else {
- if(!this.state.isServerReady){
+ if(!isServerReady){
const ready = await authenticationService.getServerReadyState();
if(ready){
- this.setState({isServerReady: true});
+ setIsServerReady(true);
}else{
- this.setState({message: "Login is currently not possible. Please re-try in a few minutes. If the problem persits, ask your administrator for assistence."});
+ setMessage("Login is currently not possible. Please re-try in a few minutes. If the problem persists, ask your administrator for assistance.");
}
}else{
- this.setState({
- message: "Could not log in. Please check your credentials or ask your administrator for assistence.",
- password: ""
- })
+ setMessage("Could not log in. Please check your credentials or ask your administrator for assistance.");
+ setPassword("");
}
}
}
+
+ const classes = styles();
+ const areProvidersAvailable = externalLoginProviders && externalLoginProviders.length > 0;
+
+ return (
+ <>
+ <CssBaseline />
+ <main className={classes.layout}>
+ <Paper className={classes.paper}>
+ <Avatar className={classes.avatar}>
+ <img src={loginIcon} alt="loginIcon" />
+ </Avatar>
+ <Typography variant="caption">Sign in</Typography>
+ <form className={classes.form}>
+ {areProvidersAvailable &&
+ <>
+ {
+ externalLoginProviders!.map((provider, index) => (
+ <Button
+ aria-controls="externalLogin"
+ aria-label={"external-login-identity-provider-" + (index + 1)}
+ aria-haspopup="true"
+ fullWidth
+ variant="contained"
+ color="inherit"
+ className={classes.submit} onClick={() => { window.location = provider.loginUrl as any; }}>
+ {provider.title}
+ </Button>))
+ }
+ <div className={classes.lineContainer}>
+ <span className={classes.thirdPartyDivider}>
+ OR
+ </span>
+ </div>
+ </>
+ }
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="username">Username</InputLabel>
+ <Input id="username" name="username" autoComplete="username" autoFocus
+ disabled={isBusy}
+ value={username}
+ onChange={event => { setUsername(event.target.value); }} />
+ </FormControl>
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="password">Password</InputLabel>
+ <Input
+ name="password"
+ type="password"
+ id="password"
+ autoComplete="current-password"
+ disabled={isBusy}
+ value={password}
+ onChange={event => { setPassword(event.target.value); }}
+ />
+ </FormControl>
+ <FormControl variant="standard" margin="normal" required fullWidth>
+ <InputLabel htmlFor="password">Domain</InputLabel>
+ <Input
+ name="scope"
+ type="scope"
+ id="scope"
+ disabled={isBusy}
+ value={scope}
+ onChange={event => { setScope(event.target.value); }}
+ />
+ </FormControl>
+ <Button
+ aria-label="login-button"
+ type="submit"
+ fullWidth
+ variant="contained"
+ color="inherit"
+ disabled={isBusy}
+ className={classes.submit}
+ onClick={onSignIn}
+ >
+ Sign in
+ </Button>
+
+ </form>
+ {message && <Alert severity="error">{message}</Alert>}
+ </Paper>
+ </main>
+ </>
+ );
}
-export const Login = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(LoginComponent)));
+export const Login = withRouter(LoginComponent);
export default Login; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/views/settings.tsx b/sdnr/wt/odlux/framework/src/views/settings.tsx
index a6b940bfa..5973db9a0 100644
--- a/sdnr/wt/odlux/framework/src/views/settings.tsx
+++ b/sdnr/wt/odlux/framework/src/views/settings.tsx
@@ -16,30 +16,18 @@
* ============LICENSE_END==========================================================================
*/
-import * as React from 'react';
-import { IApplicationStoreState } from "../store/applicationStore";
-import connect, { Connect, IDispatcher } from "../flux/connect";
+import React, {FC, useState } from 'react';
+import { useApplicationDispatch } from "../flux/connect";
-import applicationService from '../services/applicationManager';
-import { makeStyles } from '@mui/styles';
import { Divider, List, ListItem, ListItemText, Paper } from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import applicationService from '../services/applicationManager';
-import { GeneralUserSettings } from '../components/settings/general'
import { GoBackAction } from '../actions/navigationActions';
+import { GeneralUserSettings } from '../components/settings/general';
import { toAriaLabel } from '../utilities/yangHelper';
-type props = Connect<typeof mapProps, typeof mapDispatch>;
-
-type SettingsEntry = { name: string, element: JSX.Element }
-
-
-const mapProps = (state: IApplicationStoreState) => ({
-
-});
-
-const mapDispatch = (dispatcher: IDispatcher) => ({
- goBack: () => dispatcher.dispatch(new GoBackAction())
-});
+type SettingsEntry = { name: string, element: JSX.Element };
const styles = makeStyles({
sectionMargin: {
@@ -47,7 +35,6 @@ const styles = makeStyles({
marginBottom: "15px"
},
elementMargin: {
-
marginLeft: "10px"
},
menu: {
@@ -55,15 +42,17 @@ const styles = makeStyles({
}
});
-const UserSettings: React.FunctionComponent<props> = (props) => {
+const UserSettings: FC = (props) => {
- const classes = styles();
- const registrations = applicationService.applications;
+ const dispatch = useApplicationDispatch();
+ const goBack = () => dispatch(new GoBackAction());
+
+ const [selectedIndex, setSelectedIndex] = useState(0);
- const [selectedIndex, setSelectedIndex] = React.useState(0);
+ const registrations = applicationService.applications;
const navigateBack = () => {
- props.goBack();
+ goBack();
}
let settingsArray: SettingsEntry[] = [];
@@ -71,7 +60,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
//add all framework specific settings
settingsArray.push({name:"General", element: <GeneralUserSettings onClose={navigateBack} />})
-
//get app settings
let settingsElements : (SettingsEntry) [] = Object.keys(registrations).map(p => {
const application = registrations[p];
@@ -93,6 +81,8 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
setSelectedIndex(newValue);
}
+ const classes = styles();
+
return <div style={{ display: "flex", flexDirection: "row", height: "100%" }}>
<div style={{ display: "flex", flexDirection: "column", height: "100%", width: "15%" }}>
<Paper variant="outlined" style={{ height: "70%" }}>
@@ -101,7 +91,7 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
settingsArray.map((el, index) => {
return (
<>
- <ListItem selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
+ <ListItem key={"settings-key-"+index} selected={selectedIndex === index} button onClick={e => { onSelectElement(e, index) }} aria-label={toAriaLabel(el?.name+"-settings")}>
<ListItemText primary={el?.name} style={{ padding: 0 }} />
</ListItem>
<Divider />
@@ -110,7 +100,6 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
}
</List>
</Paper>
-
</div>
<div style={{ height: "100%", width: "80%", marginLeft: 15 }}>
<div style={{ height: "100%" }}>
@@ -123,4 +112,4 @@ const UserSettings: React.FunctionComponent<props> = (props) => {
}
-export default connect(mapProps, mapDispatch)(UserSettings);
+export default UserSettings; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src2/main/resources/version.json b/sdnr/wt/odlux/framework/src2/main/resources/version.json
index f9e974672..1d9af9029 100644
--- a/sdnr/wt/odlux/framework/src2/main/resources/version.json
+++ b/sdnr/wt/odlux/framework/src2/main/resources/version.json
@@ -9,8 +9,10 @@
"faultApp":"##odlux.apps.faultApp.buildno##",
"helpApp":"##odlux.apps.helpApp.buildno##",
"inventoryApp":"##odlux.apps.inventoryApp.buildno##",
+ "linkCalculationApp":"##odlux.apps.linkCalculationApp.buildno##",
"maintenanceApp":"##odlux.apps.maintenanceApp.buildno##",
"mediatorApp":"##odlux.apps.mediatorApp.buildno##",
+ "networkMapApp":"##odlux.apps.networkMapApp.buildno##",
"permanceHistoryApp":"##odlux.apps.permanceHistoryApp.buildno##"
}