mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
				synced 2025-10-30 18:27:53 +00:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			dev
			...
			feature/do
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 1dfd8d8fb1 | ||
|   | 93c57ecfb6 | ||
|   | 666f1a0483 | ||
|   | 1b8abac84b | ||
|   | 3de2ffb1a1 | ||
|   | 419b8c84cb | ||
|   | c9ef0b9046 | ||
|   | 7dc2ba9264 | ||
|   | a2fc111d8c | ||
|   | 1b47daced3 | ||
|   | ad2be40718 | ||
|   | 0f1d251c3a | ||
|   | 1bdb30ef01 | ||
|   | dcc37a113f | ||
|   | 8d4d90197d | 
							
								
								
									
										27
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||||||
|  |  | ||||||
|  | # dependencies | ||||||
|  | /node_modules | ||||||
|  | /.pnp | ||||||
|  | .pnp.js | ||||||
|  |  | ||||||
|  | # testing | ||||||
|  | /coverage | ||||||
|  |  | ||||||
|  | # production | ||||||
|  | /build | ||||||
|  |  | ||||||
|  | # misc | ||||||
|  | .DS_Store | ||||||
|  | .env.local | ||||||
|  | .env.development.local | ||||||
|  | .env.test.local | ||||||
|  | .env.production.local | ||||||
|  |  | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
|  |  | ||||||
|  | .git | ||||||
|  | .github | ||||||
|  | Dockerfile | ||||||
							
								
								
									
										2
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								.env
									
									
									
									
									
								
							| @@ -1,2 +0,0 @@ | |||||||
| REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001 |  | ||||||
| REACT_APP_ALLOW_GATEWAY_CHANGE=false |  | ||||||
							
								
								
									
										14
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | FROM node:16-alpine3.11 AS build | ||||||
|  |  | ||||||
|  | COPY package.json package-lock.json / | ||||||
|  |  | ||||||
|  | RUN npm install | ||||||
|  |  | ||||||
|  | COPY . . | ||||||
|  |  | ||||||
|  | RUN echo '{"DEFAULT_GATEWAY_URL": "https://ucentral.dpaas.arilia.com:16001","ALLOW_GATEWAY_CHANGE": true}' > public/config.json \ | ||||||
|  |     && npm run build | ||||||
|  |  | ||||||
|  | FROM nginx:1.20.1-alpine AS runtime | ||||||
|  |  | ||||||
|  | COPY --from=build /build/ /usr/share/nginx/html/ | ||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -4,6 +4,8 @@ | |||||||
| The uCentralGW Client is a user interface that lets you monitor and manage devices connected to the [uCentral gateway](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw). To use the interface, | The uCentralGW Client is a user interface that lets you monitor and manage devices connected to the [uCentral gateway](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw). To use the interface, | ||||||
| you either need to run it on your machine for [development](#development) or build it for [production](#production). | you either need to run it on your machine for [development](#development) or build it for [production](#production). | ||||||
|  |  | ||||||
|  | NOTE: This UI will be evolving as micro services are added to the uCentral program most notably with provisioning, base dashboard, firmware, device management | ||||||
|  |  | ||||||
| ## Running the solution | ## Running the solution | ||||||
|  |  | ||||||
| ### Development | ### Development | ||||||
| @@ -22,15 +24,3 @@ cd wlan-cloud-ucentralgw-ui | |||||||
| npm run build | npm run build | ||||||
| ``` | ``` | ||||||
| Once the build is done, you can move the `build` folder on your server. | Once the build is done, you can move the `build` folder on your server. | ||||||
|  |  | ||||||
| ### Environment variables |  | ||||||
| There are two environment variables currently used to control the gateway URL and also controlling if the users can modify the gateway URL. You can modify these values in the `.env` file located in the root of the project.  |  | ||||||
|  |  | ||||||
| During development, you will need to stop and start the project again to see those changes come into effect. |  | ||||||
| ```asm |  | ||||||
| REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001 |  | ||||||
| REACT_APP_ALLOW_GATEWAY_CHANGE=false |  | ||||||
| ``` |  | ||||||
| - `REACT_APP_DEFAULT_GATEWAY_URL` points to the actual uCentral gateway, including the port. |  | ||||||
| - `REACT_APP_ALLOW_GATEWAY_CHANGE` : when set to `true` will allow a user to change the gateway name she wants to use. When set to `false`, will not show a text field for the gateway and will only allow users to go to the gateway speficied in `REACT_APP_DEFAULT_GATEWAY_URL`. |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,11 +1,12 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "0.9.0", |   "version": "0.9.3", | ||||||
|   "lockfileVersion": 2, |   "lockfileVersion": 2, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "version": "0.9.0", |       "name": "ucentral-client", | ||||||
|  |       "version": "0.9.2", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@coreui/coreui": "^3.4.0", |         "@coreui/coreui": "^3.4.0", | ||||||
|         "@coreui/icons": "^2.0.1", |         "@coreui/icons": "^2.0.1", | ||||||
| @@ -37,6 +38,7 @@ | |||||||
|         "react-select": "^4.3.1", |         "react-select": "^4.3.1", | ||||||
|         "react-widgets": "^5.1.1", |         "react-widgets": "^5.1.1", | ||||||
|         "redux": "^4.1.0", |         "redux": "^4.1.0", | ||||||
|  |         "sass": "^1.35.1", | ||||||
|         "uuid": "^8.3.2" |         "uuid": "^8.3.2" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
| @@ -5248,7 +5250,6 @@ | |||||||
|       "version": "2.2.0", |       "version": "2.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", |       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", | ||||||
|       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", |       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", | ||||||
|       "optional": true, |  | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
|       } |       } | ||||||
| @@ -5737,7 +5738,6 @@ | |||||||
|       "version": "3.5.1", |       "version": "3.5.1", | ||||||
|       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", |       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", | ||||||
|       "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", |       "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", | ||||||
|       "optional": true, |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "anymatch": "~3.1.1", |         "anymatch": "~3.1.1", | ||||||
|         "braces": "~3.0.2", |         "braces": "~3.0.2", | ||||||
| @@ -10569,7 +10569,6 @@ | |||||||
|       "version": "2.1.0", |       "version": "2.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", |       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", | ||||||
|       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", |       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", | ||||||
|       "optional": true, |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "binary-extensions": "^2.0.0" |         "binary-extensions": "^2.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -17453,7 +17452,6 @@ | |||||||
|       "version": "3.5.0", |       "version": "3.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", |       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", | ||||||
|       "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", |       "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", | ||||||
|       "optional": true, |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "picomatch": "^2.2.1" |         "picomatch": "^2.2.1" | ||||||
|       }, |       }, | ||||||
| @@ -18343,8 +18341,6 @@ | |||||||
|       "version": "1.35.1", |       "version": "1.35.1", | ||||||
|       "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", |       "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", | ||||||
|       "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", |       "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", | ||||||
|       "optional": true, |  | ||||||
|       "peer": true, |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "chokidar": ">=3.0.0 <4.0.0" |         "chokidar": ">=3.0.0 <4.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -26490,8 +26486,7 @@ | |||||||
|     "binary-extensions": { |     "binary-extensions": { | ||||||
|       "version": "2.2.0", |       "version": "2.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", |       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", | ||||||
|       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", |       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" | ||||||
|       "optional": true |  | ||||||
|     }, |     }, | ||||||
|     "bindings": { |     "bindings": { | ||||||
|       "version": "1.5.0", |       "version": "1.5.0", | ||||||
| @@ -26896,7 +26891,6 @@ | |||||||
|       "version": "3.5.1", |       "version": "3.5.1", | ||||||
|       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", |       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", | ||||||
|       "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", |       "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", | ||||||
|       "optional": true, |  | ||||||
|       "requires": { |       "requires": { | ||||||
|         "anymatch": "~3.1.1", |         "anymatch": "~3.1.1", | ||||||
|         "braces": "~3.0.2", |         "braces": "~3.0.2", | ||||||
| @@ -30706,7 +30700,6 @@ | |||||||
|       "version": "2.1.0", |       "version": "2.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", |       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", | ||||||
|       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", |       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", | ||||||
|       "optional": true, |  | ||||||
|       "requires": { |       "requires": { | ||||||
|         "binary-extensions": "^2.0.0" |         "binary-extensions": "^2.0.0" | ||||||
|       } |       } | ||||||
| @@ -36047,7 +36040,6 @@ | |||||||
|       "version": "3.5.0", |       "version": "3.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", |       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", | ||||||
|       "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", |       "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", | ||||||
|       "optional": true, |  | ||||||
|       "requires": { |       "requires": { | ||||||
|         "picomatch": "^2.2.1" |         "picomatch": "^2.2.1" | ||||||
|       } |       } | ||||||
| @@ -36756,8 +36748,6 @@ | |||||||
|       "version": "1.35.1", |       "version": "1.35.1", | ||||||
|       "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", |       "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", | ||||||
|       "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", |       "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", | ||||||
|       "optional": true, |  | ||||||
|       "peer": true, |  | ||||||
|       "requires": { |       "requires": { | ||||||
|         "chokidar": ">=3.0.0 <4.0.0" |         "chokidar": ">=3.0.0 <4.0.0" | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ucentral-client", |   "name": "ucentral-client", | ||||||
|   "version": "0.9.0", |   "version": "0.9.3", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@coreui/coreui": "^3.4.0", |     "@coreui/coreui": "^3.4.0", | ||||||
| @@ -33,6 +33,7 @@ | |||||||
|     "react-select": "^4.3.1", |     "react-select": "^4.3.1", | ||||||
|     "react-widgets": "^5.1.1", |     "react-widgets": "^5.1.1", | ||||||
|     "redux": "^4.1.0", |     "redux": "^4.1.0", | ||||||
|  |     "sass": "^1.35.1", | ||||||
|     "uuid": "^8.3.2" |     "uuid": "^8.3.2" | ||||||
|   }, |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								public/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								public/config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | { | ||||||
|  |   "DEFAULT_GATEWAY_URL": "https://ucentral.dpaas.arilia.com:16001", | ||||||
|  |   "ALLOW_GATEWAY_CHANGE": false | ||||||
|  | } | ||||||
| @@ -103,6 +103,12 @@ | |||||||
| 		"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.", | 		"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.", | ||||||
| 		"title": "Befehl löschen" | 		"title": "Befehl löschen" | ||||||
| 	}, | 	}, | ||||||
|  | 	"delete_logs": { | ||||||
|  | 		"date": "Wählen Sie das Datum des ältesten Protokolls aus, das Sie behalten möchten", | ||||||
|  | 		"device_logs_title": "Geräteprotokolle löschen", | ||||||
|  | 		"explanation": "Dadurch werden alle {{object}} vor dem von Ihnen gewählten Datum gelöscht. Seien Sie vorsichtig, diese Aktion ist nicht umkehrbar.", | ||||||
|  | 		"healthchecks_title": "Healthchecks löschen" | ||||||
|  | 	}, | ||||||
| 	"device_logs": { | 	"device_logs": { | ||||||
| 		"log": "Log", | 		"log": "Log", | ||||||
| 		"severity": "Schwere", | 		"severity": "Schwere", | ||||||
|   | |||||||
| @@ -103,6 +103,12 @@ | |||||||
| 		"explanation": "Are you sure you want to delete this command? This action is not reversible.", | 		"explanation": "Are you sure you want to delete this command? This action is not reversible.", | ||||||
| 		"title": "Delete Command" | 		"title": "Delete Command" | ||||||
| 	}, | 	}, | ||||||
|  | 	"delete_logs": { | ||||||
|  | 		"date": "Select the date of the oldest log you would like to keep", | ||||||
|  | 		"device_logs_title": "Delete Device Logs", | ||||||
|  | 		"explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.", | ||||||
|  | 		"healthchecks_title": "Delete Healthchecks" | ||||||
|  | 	}, | ||||||
| 	"device_logs": { | 	"device_logs": { | ||||||
| 		"log": "Log", | 		"log": "Log", | ||||||
| 		"severity": "Severity", | 		"severity": "Severity", | ||||||
|   | |||||||
| @@ -103,6 +103,12 @@ | |||||||
| 		"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.", | 		"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.", | ||||||
| 		"title": "Eliminar comando" | 		"title": "Eliminar comando" | ||||||
| 	}, | 	}, | ||||||
|  | 	"delete_logs": { | ||||||
|  | 		"date": "Seleccione la fecha del registro más antiguo que le gustaría conservar", | ||||||
|  | 		"device_logs_title": "Eliminar registros de dispositivos", | ||||||
|  | 		"explanation": "Esto eliminará todos los {{object}} antes de la fecha que elija. Tenga cuidado, esta acción no es reversible.", | ||||||
|  | 		"healthchecks_title": "Eliminar comprobaciones de estado" | ||||||
|  | 	}, | ||||||
| 	"device_logs": { | 	"device_logs": { | ||||||
| 		"log": "Iniciar sesión", | 		"log": "Iniciar sesión", | ||||||
| 		"severity": "Gravedad", | 		"severity": "Gravedad", | ||||||
|   | |||||||
| @@ -103,6 +103,12 @@ | |||||||
| 		"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.", | 		"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.", | ||||||
| 		"title": "Supprimer la commande" | 		"title": "Supprimer la commande" | ||||||
| 	}, | 	}, | ||||||
|  | 	"delete_logs": { | ||||||
|  | 		"date": "Sélectionnez la date du plus ancien journal que vous souhaitez conserver", | ||||||
|  | 		"device_logs_title": "Supprimer les journaux de l'appareil", | ||||||
|  | 		"explanation": "Cela supprimera tous les {{object}} avant la date que vous choisissez. Attention, cette action n'est pas réversible.", | ||||||
|  | 		"healthchecks_title": "Supprimer les vérifications d'état" | ||||||
|  | 	}, | ||||||
| 	"device_logs": { | 	"device_logs": { | ||||||
| 		"log": "Bûche", | 		"log": "Bûche", | ||||||
| 		"severity": "Gravité", | 		"severity": "Gravité", | ||||||
|   | |||||||
| @@ -103,6 +103,12 @@ | |||||||
| 		"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.", | 		"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.", | ||||||
| 		"title": "Apagar Comando" | 		"title": "Apagar Comando" | ||||||
| 	}, | 	}, | ||||||
|  | 	"delete_logs": { | ||||||
|  | 		"date": "Selecione a data do registro mais antigo que você gostaria de manter", | ||||||
|  | 		"device_logs_title": "Excluir registros do dispositivo", | ||||||
|  | 		"explanation": "Isso excluirá todos os {{object}} antes da data que você escolheu. Cuidado, esta ação não é reversível.", | ||||||
|  | 		"healthchecks_title": "Excluir verificações de saúde" | ||||||
|  | 	}, | ||||||
| 	"device_logs": { | 	"device_logs": { | ||||||
| 		"log": "Registro", | 		"log": "Registro", | ||||||
| 		"severity": "Gravidade", | 		"severity": "Gravidade", | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								src/components/ConfirmFooter/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/components/ConfirmFooter/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | |||||||
|  | import React, { useState, useEffect } from 'react'; | ||||||
|  | import { useTranslation } from 'react-i18next'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { CButton, CSpinner, CModalFooter } from '@coreui/react'; | ||||||
|  |  | ||||||
|  | const ConfirmFooter = ({ isShown, isLoading, action, color, variant, block, toggleParent }) => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   const [askingIfSure, setAskingIfSure] = useState(false); | ||||||
|  |  | ||||||
|  |   const confirmingIfSure = () => { | ||||||
|  |     setAskingIfSure(true); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     setAskingIfSure(false); | ||||||
|  |   }, [isShown]); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <CModalFooter> | ||||||
|  |       <div hidden={!askingIfSure}>{t('common.are_you_sure')}</div> | ||||||
|  |       <CButton | ||||||
|  |         disabled={isLoading} | ||||||
|  |         hidden={askingIfSure} | ||||||
|  |         color={color} | ||||||
|  |         variant={variant} | ||||||
|  |         onClick={() => confirmingIfSure()} | ||||||
|  |         block={block} | ||||||
|  |       > | ||||||
|  |         {t('common.submit')} | ||||||
|  |       </CButton> | ||||||
|  |       <CButton | ||||||
|  |         disabled={isLoading} | ||||||
|  |         hidden={!askingIfSure} | ||||||
|  |         color={color} | ||||||
|  |         onClick={() => action()} | ||||||
|  |         block={block} | ||||||
|  |       > | ||||||
|  |         {isLoading ? t('common.loading_ellipsis') : t('common.yes')} | ||||||
|  |         <CSpinner color="light" hidden={!isLoading} component="span" size="sm" /> | ||||||
|  |       </CButton> | ||||||
|  |       <CButton color="secondary" onClick={toggleParent}> | ||||||
|  |         {t('common.cancel')} | ||||||
|  |       </CButton> | ||||||
|  |     </CModalFooter> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | ConfirmFooter.propTypes = { | ||||||
|  |   isLoading: PropTypes.bool.isRequired, | ||||||
|  |   block: PropTypes.bool, | ||||||
|  |   action: PropTypes.func.isRequired, | ||||||
|  |   color: PropTypes.string, | ||||||
|  |   variant: PropTypes.string, | ||||||
|  |   toggleParent: PropTypes.func.isRequired, | ||||||
|  |   isShown: PropTypes.bool.isRequired, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | ConfirmFooter.defaultProps = { | ||||||
|  |   color: 'primary', | ||||||
|  |   variant: '', | ||||||
|  |   block: false, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default ConfirmFooter; | ||||||
							
								
								
									
										100
									
								
								src/components/DeleteLogModal/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/components/DeleteLogModal/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | import React, { useState, useEffect } from 'react'; | ||||||
|  | import { useTranslation } from 'react-i18next'; | ||||||
|  | import { CModal, CModalHeader, CModalTitle, CModalBody, CCol, CRow } from '@coreui/react'; | ||||||
|  | import DatePicker from 'react-widgets/DatePicker'; | ||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import ConfirmFooter from 'components/ConfirmFooter'; | ||||||
|  | import { dateToUnix } from 'utils/helper'; | ||||||
|  | import axiosInstance from 'utils/axiosInstance'; | ||||||
|  | import { getToken } from 'utils/authHelper'; | ||||||
|  | import eventBus from 'utils/eventBus'; | ||||||
|  | import styles from './index.module.scss'; | ||||||
|  |  | ||||||
|  | const DeleteLogModal = ({ serialNumber, show, toggle, object }) => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   const [loading, setLoading] = useState(false); | ||||||
|  |   const [maxDate, setMaxDate] = useState(new Date().toString()); | ||||||
|  |  | ||||||
|  |   const setDate = (date) => { | ||||||
|  |     if (date) { | ||||||
|  |       setMaxDate(date.toString()); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const deleteLog = async () => { | ||||||
|  |     setLoading(true); | ||||||
|  |  | ||||||
|  |     const options = { | ||||||
|  |       headers: { | ||||||
|  |         Accept: 'application/json', | ||||||
|  |         Authorization: `Bearer ${getToken()}`, | ||||||
|  |       }, | ||||||
|  |       params: { | ||||||
|  |         endDate: dateToUnix(maxDate), | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  |     return axiosInstance | ||||||
|  |       .delete(`/device/${serialNumber}/${object}`, options) | ||||||
|  |       .then(() => {}) | ||||||
|  |       .catch(() => {}) | ||||||
|  |       .finally(() => { | ||||||
|  |         if (object === 'healthchecks') | ||||||
|  |           eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' }); | ||||||
|  |         else if (object === 'logs') | ||||||
|  |           eventBus.dispatch('deletedLogs', { message: 'Deleted device logs' }); | ||||||
|  |         setLoading(false); | ||||||
|  |         toggle(); | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     setLoading(false); | ||||||
|  |     setMaxDate(new Date().toString()); | ||||||
|  |   }, [show]); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <CModal className={styles.modal} show={show} onClose={toggle}> | ||||||
|  |       <CModalHeader closeButton> | ||||||
|  |         <CModalTitle> | ||||||
|  |           {object === 'healthchecks' | ||||||
|  |             ? t('delete_logs.healthchecks_title') | ||||||
|  |             : t('delete_logs.device_logs_title')} | ||||||
|  |         </CModalTitle> | ||||||
|  |       </CModalHeader> | ||||||
|  |       <CModalBody> | ||||||
|  |         <h6>{t('delete_logs.explanation', { object })}</h6> | ||||||
|  |         <CRow className={styles.spacedRow}> | ||||||
|  |           <CCol md="4" className={styles.spacedDate}> | ||||||
|  |             <p>{t('common.date')}:</p> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol xs="12" md="8"> | ||||||
|  |             <DatePicker | ||||||
|  |               selected={new Date(maxDate)} | ||||||
|  |               includeTime | ||||||
|  |               value={new Date(maxDate)} | ||||||
|  |               placeholder="Select custom date" | ||||||
|  |               disabled={loading} | ||||||
|  |               onChange={(date) => setDate(date)} | ||||||
|  |             /> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |       </CModalBody> | ||||||
|  |       <ConfirmFooter | ||||||
|  |         isShown={show} | ||||||
|  |         isLoading={loading} | ||||||
|  |         action={deleteLog} | ||||||
|  |         color="primary" | ||||||
|  |         toggleParent={toggle} | ||||||
|  |       /> | ||||||
|  |     </CModal> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | DeleteLogModal.propTypes = { | ||||||
|  |   show: PropTypes.bool.isRequired, | ||||||
|  |   toggle: PropTypes.func.isRequired, | ||||||
|  |   object: PropTypes.string.isRequired, | ||||||
|  |   serialNumber: PropTypes.string.isRequired, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default DeleteLogModal; | ||||||
							
								
								
									
										11
									
								
								src/components/DeleteLogModal/index.module.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/components/DeleteLogModal/index.module.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | .modal { | ||||||
|  |   color: #3c4b64; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .spacedRow { | ||||||
|  |   margin-top: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .spacedColumn { | ||||||
|  |   margin-top: 7px; | ||||||
|  | } | ||||||
| @@ -10,6 +10,7 @@ import { | |||||||
|   CRow, |   CRow, | ||||||
|   CCol, |   CCol, | ||||||
|   CProgress, |   CProgress, | ||||||
|  |   CPopover, | ||||||
| } from '@coreui/react'; | } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react'; | import CIcon from '@coreui/icons-react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| @@ -18,7 +19,9 @@ import PropTypes from 'prop-types'; | |||||||
| import { prettyDate, dateToUnix } from 'utils/helper'; | import { prettyDate, dateToUnix } from 'utils/helper'; | ||||||
| import axiosInstance from 'utils/axiosInstance'; | import axiosInstance from 'utils/axiosInstance'; | ||||||
| import { getToken } from 'utils/authHelper'; | import { getToken } from 'utils/authHelper'; | ||||||
|  | import eventBus from 'utils/eventBus'; | ||||||
| import LoadingButton from 'components/LoadingButton'; | import LoadingButton from 'components/LoadingButton'; | ||||||
|  | import DeleteLogModal from 'components/DeleteLogModal'; | ||||||
| import styles from './index.module.scss'; | import styles from './index.module.scss'; | ||||||
|  |  | ||||||
| const DeviceHealth = ({ selectedDeviceId }) => { | const DeviceHealth = ({ selectedDeviceId }) => { | ||||||
| @@ -34,6 +37,11 @@ const DeviceHealth = ({ selectedDeviceId }) => { | |||||||
|   const [showLoadingMore, setShowLoadingMore] = useState(true); |   const [showLoadingMore, setShowLoadingMore] = useState(true); | ||||||
|   const [sanityLevel, setSanityLevel] = useState(null); |   const [sanityLevel, setSanityLevel] = useState(null); | ||||||
|   const [barColor, setBarColor] = useState('gradient-dark'); |   const [barColor, setBarColor] = useState('gradient-dark'); | ||||||
|  |   const [showDeleteModal, setShowDeleteModal] = useState(false); | ||||||
|  |  | ||||||
|  |   const toggleDeleteModal = () => { | ||||||
|  |     setShowDeleteModal(!showDeleteModal); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   const toggle = (e) => { |   const toggle = (e) => { | ||||||
|     setCollapse(!collapse); |     setCollapse(!collapse); | ||||||
| @@ -167,6 +175,14 @@ const DeviceHealth = ({ selectedDeviceId }) => { | |||||||
|     } |     } | ||||||
|   }, [start, end, selectedDeviceId]); |   }, [start, end, selectedDeviceId]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     eventBus.on('deletedHealth', () => getDeviceHealth()); | ||||||
|  |  | ||||||
|  |     return () => { | ||||||
|  |       eventBus.remove('deletedHealth'); | ||||||
|  |     }; | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CWidgetDropdown |     <CWidgetDropdown | ||||||
|       header={sanityLevel ? `${sanityLevel}%` : t('common.unknown')} |       header={sanityLevel ? `${sanityLevel}%` : t('common.unknown')} | ||||||
| @@ -178,6 +194,20 @@ const DeviceHealth = ({ selectedDeviceId }) => { | |||||||
|         <div className={styles.footer}> |         <div className={styles.footer}> | ||||||
|           <CProgress className={styles.progressBar} color="white" value={sanityLevel ?? 0} /> |           <CProgress className={styles.progressBar} color="white" value={sanityLevel ?? 0} /> | ||||||
|           <CCollapse show={collapse}> |           <CCollapse show={collapse}> | ||||||
|  |             <div className={styles.alignRight}> | ||||||
|  |               <CPopover content={t('common.delete')}> | ||||||
|  |                 <CButton | ||||||
|  |                   color="light" | ||||||
|  |                   shape="square" | ||||||
|  |                   size="sm" | ||||||
|  |                   onClick={() => { | ||||||
|  |                     toggleDeleteModal(); | ||||||
|  |                   }} | ||||||
|  |                 > | ||||||
|  |                   <CIcon name="cilTrash" size="lg" /> | ||||||
|  |                 </CButton> | ||||||
|  |               </CPopover> | ||||||
|  |             </div> | ||||||
|             <CRow className={styles.spacedRow}> |             <CRow className={styles.spacedRow}> | ||||||
|               <CCol> |               <CCol> | ||||||
|                 {t('common.from')}: |                 {t('common.from')}: | ||||||
| @@ -250,6 +280,12 @@ const DeviceHealth = ({ selectedDeviceId }) => { | |||||||
|               size="lg" |               size="lg" | ||||||
|             /> |             /> | ||||||
|           </CButton> |           </CButton> | ||||||
|  |           <DeleteLogModal | ||||||
|  |             serialNumber={selectedDeviceId} | ||||||
|  |             object="healthchecks" | ||||||
|  |             show={showDeleteModal} | ||||||
|  |             toggle={toggleDeleteModal} | ||||||
|  |           /> | ||||||
|         </div> |         </div> | ||||||
|       } |       } | ||||||
|     /> |     /> | ||||||
|   | |||||||
| @@ -25,3 +25,7 @@ | |||||||
| .scrollable { | .scrollable { | ||||||
|   height: 250px; |   height: 250px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .alignRight { | ||||||
|  |   float: right; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import { | |||||||
|   CDataTable, |   CDataTable, | ||||||
|   CCard, |   CCard, | ||||||
|   CCardBody, |   CCardBody, | ||||||
|  |   CPopover, | ||||||
| } from '@coreui/react'; | } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react'; | import CIcon from '@coreui/icons-react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| @@ -17,7 +18,9 @@ import PropTypes from 'prop-types'; | |||||||
| import { prettyDate, dateToUnix } from 'utils/helper'; | import { prettyDate, dateToUnix } from 'utils/helper'; | ||||||
| import axiosInstance from 'utils/axiosInstance'; | import axiosInstance from 'utils/axiosInstance'; | ||||||
| import { getToken } from 'utils/authHelper'; | import { getToken } from 'utils/authHelper'; | ||||||
|  | import eventBus from 'utils/eventBus'; | ||||||
| import LoadingButton from 'components/LoadingButton'; | import LoadingButton from 'components/LoadingButton'; | ||||||
|  | import DeleteLogModal from 'components/DeleteLogModal'; | ||||||
| import styles from './index.module.scss'; | import styles from './index.module.scss'; | ||||||
|  |  | ||||||
| const DeviceLogs = ({ selectedDeviceId }) => { | const DeviceLogs = ({ selectedDeviceId }) => { | ||||||
| @@ -31,6 +34,11 @@ const DeviceLogs = ({ selectedDeviceId }) => { | |||||||
|   const [logLimit, setLogLimit] = useState(25); |   const [logLimit, setLogLimit] = useState(25); | ||||||
|   const [loadingMore, setLoadingMore] = useState(false); |   const [loadingMore, setLoadingMore] = useState(false); | ||||||
|   const [showLoadingMore, setShowLoadingMore] = useState(true); |   const [showLoadingMore, setShowLoadingMore] = useState(true); | ||||||
|  |   const [showDeleteModal, setShowDeleteModal] = useState(false); | ||||||
|  |  | ||||||
|  |   const toggleDeleteModal = () => { | ||||||
|  |     setShowDeleteModal(!showDeleteModal); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   const toggle = (e) => { |   const toggle = (e) => { | ||||||
|     setCollapse(!collapse); |     setCollapse(!collapse); | ||||||
| @@ -149,85 +157,115 @@ const DeviceLogs = ({ selectedDeviceId }) => { | |||||||
|     } |     } | ||||||
|   }, [start, end, selectedDeviceId]); |   }, [start, end, selectedDeviceId]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     eventBus.on('deletedLogs', () => getLogs()); | ||||||
|  |  | ||||||
|  |     return () => { | ||||||
|  |       eventBus.remove('deletedLogs'); | ||||||
|  |     }; | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CWidgetDropdown |     <div> | ||||||
|       inverse="true" |       <CWidgetDropdown | ||||||
|       color="gradient-info" |         inverse="true" | ||||||
|       header={t('device_logs.title')} |         color="gradient-info" | ||||||
|       footerSlot={ |         header={t('device_logs.title')} | ||||||
|         <div className={styles.footer}> |         footerSlot={ | ||||||
|           <CCollapse show={collapse}> |           <div className={styles.footer}> | ||||||
|             <CRow className={styles.datepickerRow}> |             <CCollapse show={collapse}> | ||||||
|               <CCol> |               <div className={styles.alignRight}> | ||||||
|                 {t('common.from')} |                 <CPopover content={t('common.delete')}> | ||||||
|                 <DatePicker includeTime onChange={(date) => modifyStart(date)} /> |                   <CButton | ||||||
|               </CCol> |                     color="light" | ||||||
|               <CCol> |                     shape="square" | ||||||
|                 {t('common.to')} |                     size="sm" | ||||||
|                 <DatePicker includeTime onChange={(date) => modifyEnd(date)} /> |                     onClick={() => { | ||||||
|               </CCol> |                       toggleDeleteModal(); | ||||||
|             </CRow> |                     }} | ||||||
|             <CCard> |                   > | ||||||
|               <div className={[styles.scrollableCard, 'overflow-auto'].join(' ')}> |                     <CIcon name="cilTrash" size="lg" /> | ||||||
|                 <CDataTable |                   </CButton> | ||||||
|                   items={logs ?? []} |                 </CPopover> | ||||||
|                   fields={columns} |  | ||||||
|                   loading={loading} |  | ||||||
|                   className={styles.whiteIcon} |  | ||||||
|                   sorterValue={{ column: 'recorded', desc: 'true' }} |  | ||||||
|                   scopedSlots={{ |  | ||||||
|                     recorded: (item) => <td>{prettyDate(item.recorded)}</td>, |  | ||||||
|                     show_details: (item, index) => ( |  | ||||||
|                       <td className="py-2"> |  | ||||||
|                         <CButton |  | ||||||
|                           color="primary" |  | ||||||
|                           variant={details.includes(index) ? '' : 'outline'} |  | ||||||
|                           shape="square" |  | ||||||
|                           size="sm" |  | ||||||
|                           onClick={() => { |  | ||||||
|                             toggleDetails(index); |  | ||||||
|                           }} |  | ||||||
|                         > |  | ||||||
|                           <CIcon name="cilList" size="lg" /> |  | ||||||
|                         </CButton> |  | ||||||
|                       </td> |  | ||||||
|                     ), |  | ||||||
|                     details: (item, index) => ( |  | ||||||
|                       <CCollapse show={details.includes(index)}> |  | ||||||
|                         <CCardBody> |  | ||||||
|                           <h5>{t('common.details')}</h5> |  | ||||||
|                           <div>{getDetails(index, item)}</div> |  | ||||||
|                         </CCardBody> |  | ||||||
|                       </CCollapse> |  | ||||||
|                     ), |  | ||||||
|                   }} |  | ||||||
|                 /> |  | ||||||
|                 <CRow className={styles.loadMoreRow}> |  | ||||||
|                   {showLoadingMore && ( |  | ||||||
|                     <LoadingButton |  | ||||||
|                       label={t('common.view_more')} |  | ||||||
|                       isLoadingLabel={t('common.loading_more_ellipsis')} |  | ||||||
|                       isLoading={loadingMore} |  | ||||||
|                       action={showMoreLogs} |  | ||||||
|                       variant="outline" |  | ||||||
|                     /> |  | ||||||
|                   )} |  | ||||||
|                 </CRow> |  | ||||||
|               </div> |               </div> | ||||||
|             </CCard> |               <CRow className={styles.datepickerRow}> | ||||||
|           </CCollapse> |                 <CCol> | ||||||
|           <CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block> |                   {t('common.from')} | ||||||
|             <CIcon |                   <DatePicker includeTime onChange={(date) => modifyStart(date)} /> | ||||||
|               name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} |                 </CCol> | ||||||
|               className={styles.whiteIcon} |                 <CCol> | ||||||
|               size="lg" |                   {t('common.to')} | ||||||
|             /> |                   <DatePicker includeTime onChange={(date) => modifyEnd(date)} /> | ||||||
|           </CButton> |                 </CCol> | ||||||
|         </div> |               </CRow> | ||||||
|       } |               <CCard> | ||||||
|     > |                 <div className={[styles.scrollableCard, 'overflow-auto'].join(' ')}> | ||||||
|       <CIcon name="cilList" className={styles.whiteIcon} size="lg" /> |                   <CDataTable | ||||||
|     </CWidgetDropdown> |                     items={logs ?? []} | ||||||
|  |                     fields={columns} | ||||||
|  |                     loading={loading} | ||||||
|  |                     className={styles.whiteIcon} | ||||||
|  |                     sorterValue={{ column: 'recorded', desc: 'true' }} | ||||||
|  |                     scopedSlots={{ | ||||||
|  |                       recorded: (item) => <td>{prettyDate(item.recorded)}</td>, | ||||||
|  |                       show_details: (item, index) => ( | ||||||
|  |                         <td className="py-2"> | ||||||
|  |                           <CButton | ||||||
|  |                             color="primary" | ||||||
|  |                             variant={details.includes(index) ? '' : 'outline'} | ||||||
|  |                             shape="square" | ||||||
|  |                             size="sm" | ||||||
|  |                             onClick={() => { | ||||||
|  |                               toggleDetails(index); | ||||||
|  |                             }} | ||||||
|  |                           > | ||||||
|  |                             <CIcon name="cilList" size="lg" /> | ||||||
|  |                           </CButton> | ||||||
|  |                         </td> | ||||||
|  |                       ), | ||||||
|  |                       details: (item, index) => ( | ||||||
|  |                         <CCollapse show={details.includes(index)}> | ||||||
|  |                           <CCardBody> | ||||||
|  |                             <h5>{t('common.details')}</h5> | ||||||
|  |                             <div>{getDetails(index, item)}</div> | ||||||
|  |                           </CCardBody> | ||||||
|  |                         </CCollapse> | ||||||
|  |                       ), | ||||||
|  |                     }} | ||||||
|  |                   /> | ||||||
|  |                   <CRow className={styles.loadMoreRow}> | ||||||
|  |                     {showLoadingMore && ( | ||||||
|  |                       <LoadingButton | ||||||
|  |                         label={t('common.view_more')} | ||||||
|  |                         isLoadingLabel={t('common.loading_more_ellipsis')} | ||||||
|  |                         isLoading={loadingMore} | ||||||
|  |                         action={showMoreLogs} | ||||||
|  |                         variant="outline" | ||||||
|  |                       /> | ||||||
|  |                     )} | ||||||
|  |                   </CRow> | ||||||
|  |                 </div> | ||||||
|  |               </CCard> | ||||||
|  |             </CCollapse> | ||||||
|  |             <CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block> | ||||||
|  |               <CIcon | ||||||
|  |                 name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} | ||||||
|  |                 className={styles.whiteIcon} | ||||||
|  |                 size="lg" | ||||||
|  |               /> | ||||||
|  |             </CButton> | ||||||
|  |           </div> | ||||||
|  |         } | ||||||
|  |       > | ||||||
|  |         <CIcon name="cilList" className={styles.whiteIcon} size="lg" /> | ||||||
|  |       </CWidgetDropdown> | ||||||
|  |       <DeleteLogModal | ||||||
|  |         serialNumber={selectedDeviceId} | ||||||
|  |         object="logs" | ||||||
|  |         show={showDeleteModal} | ||||||
|  |         toggle={toggleDeleteModal} | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,3 +17,7 @@ | |||||||
| .loadMoreRow { | .loadMoreRow { | ||||||
|   margin-bottom: 1%; |   margin-bottom: 1%; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .alignRight { | ||||||
|  |   float: right; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import Chart from 'react-apexcharts'; | import Chart from 'react-apexcharts'; | ||||||
| import styles from './index.module.scss'; |  | ||||||
|  |  | ||||||
| const DeviceStatisticsChart = ({ data, options }) => ( | const DeviceStatisticsChart = ({ data, options }) => ( | ||||||
|   <div className={styles.chart}> |   <div style={{ height: '360px' }}> | ||||||
|     <Chart series={data} options={options} type="line" height="100%" /> |     <Chart series={data} options={options} type="line" height="100%" /> | ||||||
|   </div> |   </div> | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -1,3 +0,0 @@ | |||||||
| .chart { |  | ||||||
|   height: 360px; |  | ||||||
| } |  | ||||||
| @@ -10,8 +10,10 @@ import DeviceStatisticsChart from '../DeviceStatisticsChart'; | |||||||
| const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => { | const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const [loading, setLoading] = useState(false); |   const [loading, setLoading] = useState(false); | ||||||
|   const [deviceStats, setStats] = useState([]); |   const [statOptions, setStatOptions] = useState({ | ||||||
|   const [statOptions, setStatOptions] = useState({}); |     interfaceList: [], | ||||||
|  |     settings: {}, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   const transformIntoDataset = (data) => { |   const transformIntoDataset = (data) => { | ||||||
|     const sortedData = data.sort((a, b) => { |     const sortedData = data.sort((a, b) => { | ||||||
| @@ -60,7 +62,9 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => { | |||||||
|         interfaceList[interfaceTypes[inter.name]][0].data.push( |         interfaceList[interfaceTypes[inter.name]][0].data.push( | ||||||
|           Math.floor(inter.counters.tx_bytes / 1024), |           Math.floor(inter.counters.tx_bytes / 1024), | ||||||
|         ); |         ); | ||||||
|         interfaceList[interfaceTypes[inter.name]][1].data.push(Math.floor(inter.counters.rx_bytes)); |         interfaceList[interfaceTypes[inter.name]][1].data.push( | ||||||
|  |           Math.floor(inter.counters.rx_bytes / 1024), | ||||||
|  |         ); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -97,8 +101,14 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => { | |||||||
|       }, |       }, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     setStatOptions(options); |     const newOptions = { | ||||||
|     setStats(interfaceList); |       interfaceList, | ||||||
|  |       settings: options, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (statOptions !== newOptions) { | ||||||
|  |       setStatOptions(newOptions); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const getStatistics = () => { |   const getStatistics = () => { | ||||||
| @@ -134,20 +144,20 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => { | |||||||
|   }, [selectedDeviceId]); |   }, [selectedDeviceId]); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (lastRefresh !== '' && selectedDeviceId) { |     if (!loading && lastRefresh !== '' && selectedDeviceId) { | ||||||
|       getStatistics(); |       getStatistics(); | ||||||
|     } |     } | ||||||
|   }, [lastRefresh]); |   }, [lastRefresh]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div> |     <div> | ||||||
|       {deviceStats.map((data) => ( |       {statOptions.interfaceList.map((data) => ( | ||||||
|         <div key={createUuid()}> |         <div key={createUuid()}> | ||||||
|           <DeviceStatisticsChart |           <DeviceStatisticsChart | ||||||
|             key={createUuid()} |             key={createUuid()} | ||||||
|             data={data} |             data={data} | ||||||
|             options={{ |             options={{ | ||||||
|               ...statOptions, |               ...statOptions.settings, | ||||||
|               title: { |               title: { | ||||||
|                 text: capitalizeFirstLetter(data[0].titleName), |                 text: capitalizeFirstLetter(data[0].titleName), | ||||||
|                 align: 'left', |                 align: 'left', | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import styles from './index.module.scss'; | |||||||
|  |  | ||||||
| const DeviceStatisticsCard = ({ selectedDeviceId }) => { | const DeviceStatisticsCard = ({ selectedDeviceId }) => { | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
|   const [lastRefresh, setLastRefresh] = useState(''); |   const [lastRefresh, setLastRefresh] = useState(new Date().toString()); | ||||||
|  |  | ||||||
|   const refresh = () => { |   const refresh = () => { | ||||||
|     setLastRefresh(new Date().toString()); |     setLastRefresh(new Date().toString()); | ||||||
|   | |||||||
| @@ -1,3 +0,0 @@ | |||||||
| { |  | ||||||
|   "REACT_APP_BASE_URL": "https://ucentral.dpaas.arilia.com:16001/api/v1" |  | ||||||
| } |  | ||||||
| @@ -6,7 +6,7 @@ const TheFooter = () => ( | |||||||
|   <Translation> |   <Translation> | ||||||
|     {(t) => ( |     {(t) => ( | ||||||
|       <CFooter fixed={false}> |       <CFooter fixed={false}> | ||||||
|         <div>{t('footer.version')} 0.9.0</div> |         <div>{t('footer.version')} 0.9.3</div> | ||||||
|         <div className="mfs-auto"> |         <div className="mfs-auto"> | ||||||
|           <span className="mr-1">{t('footer.powered_by')}</span> |           <span className="mr-1">{t('footer.powered_by')}</span> | ||||||
|           <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> |           <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| .sidebarImgFull { | .sidebarImgFull { | ||||||
|   height: 75px; |   height: 75px; | ||||||
|  |   width: 75px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .sidebarImgMinimized { | .sidebarImgMinimized { | ||||||
|   | |||||||
| @@ -30,15 +30,30 @@ const Login = () => { | |||||||
|   const dispatch = useDispatch(); |   const dispatch = useDispatch(); | ||||||
|   const [userId, setUsername] = useState(''); |   const [userId, setUsername] = useState(''); | ||||||
|   const [password, setPassword] = useState(''); |   const [password, setPassword] = useState(''); | ||||||
|   const [gatewayUrl, setGatewayUrl] = useState(process.env.REACT_APP_DEFAULT_GATEWAY_URL); |   const [gatewayUrl, setGatewayUrl] = useState(''); | ||||||
|   const [hadError, setHadError] = useState(false); |   const [hadError, setHadError] = useState(false); | ||||||
|   const [emptyUsername, setEmptyUsername] = useState(false); |   const [emptyUsername, setEmptyUsername] = useState(false); | ||||||
|   const [emptyPassword, setEmptyPassword] = useState(false); |   const [emptyPassword, setEmptyPassword] = useState(false); | ||||||
|   const [emptyGateway, setEmptyGateway] = useState(false); |   const [emptyGateway, setEmptyGateway] = useState(false); | ||||||
|   const placeholderUrl = 'Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001)'; |   const [defaultConfig, setDefaultConfig] = useState({ | ||||||
|   const defaultGatewayUrl = process.env.REACT_APP_DEFAULT_GATEWAY_URL; |     DEFAULT_GATEWAY_URL: '', | ||||||
|   const allowUrlChange = process.env.REACT_APP_ALLOW_GATEWAY_CHANGE === 'true'; |     ALLOW_GATEWAY_CHANGE: true, | ||||||
|   const loginErrorText = t('login.login_error'); |   }); | ||||||
|  |   const placeholderUrl = 'Gateway URL (ex: https://your-url:port)'; | ||||||
|  |  | ||||||
|  |   const getDefaultConfig = async () => { | ||||||
|  |     fetch('./config.json', { | ||||||
|  |       headers: { | ||||||
|  |         'Content-Type': 'application/json', | ||||||
|  |         Accept: 'application/json', | ||||||
|  |       }, | ||||||
|  |     }) | ||||||
|  |       .then((response) => response.json()) | ||||||
|  |       .then((json) => { | ||||||
|  |         setDefaultConfig(json); | ||||||
|  |       }) | ||||||
|  |       .catch(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   const formValidation = () => { |   const formValidation = () => { | ||||||
|     setHadError(false); |     setHadError(false); | ||||||
| @@ -59,12 +74,13 @@ const Login = () => { | |||||||
|       setEmptyGateway(true); |       setEmptyGateway(true); | ||||||
|       isSuccessful = false; |       isSuccessful = false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return isSuccessful; |     return isSuccessful; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const SignIn = (credentials) => { |   const SignIn = (credentials) => { | ||||||
|     const gatewayUrlToUse = allowUrlChange ? gatewayUrl : defaultGatewayUrl; |     const gatewayUrlToUse = defaultConfig.ALLOW_GATEWAY_CHANGE | ||||||
|  |       ? gatewayUrl | ||||||
|  |       : defaultConfig.DEFAULT_GATEWAY_URL; | ||||||
|  |  | ||||||
|     axiosInstance |     axiosInstance | ||||||
|       .post(`${gatewayUrlToUse}/api/v1/oauth2`, credentials) |       .post(`${gatewayUrlToUse}/api/v1/oauth2`, credentials) | ||||||
| @@ -93,6 +109,12 @@ const Login = () => { | |||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (emptyGateway) setEmptyGateway(false); |     if (emptyGateway) setEmptyGateway(false); | ||||||
|   }, [gatewayUrl]); |   }, [gatewayUrl]); | ||||||
|  |   useEffect(() => { | ||||||
|  |     getDefaultConfig(); | ||||||
|  |   }, []); | ||||||
|  |   useEffect(() => { | ||||||
|  |     setGatewayUrl(defaultConfig.DEFAULT_GATEWAY_URL); | ||||||
|  |   }, [defaultConfig]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="c-app c-default-layout flex-row align-items-center"> |     <div className="c-app c-default-layout flex-row align-items-center"> | ||||||
| @@ -151,7 +173,7 @@ const Login = () => { | |||||||
|                         {t('login.please_enter_password')} |                         {t('login.please_enter_password')} | ||||||
|                       </CInvalidFeedback> |                       </CInvalidFeedback> | ||||||
|                     </CInputGroup> |                     </CInputGroup> | ||||||
|                     <CInputGroup className="mb-4" hidden={!allowUrlChange}> |                     <CInputGroup className="mb-4" hidden={!defaultConfig.ALLOW_GATEWAY_CHANGE}> | ||||||
|                       <CPopover content="Gateway URL"> |                       <CPopover content="Gateway URL"> | ||||||
|                         <CInputGroupPrepend> |                         <CInputGroupPrepend> | ||||||
|                           <CInputGroupText> |                           <CInputGroupText> | ||||||
| @@ -175,7 +197,7 @@ const Login = () => { | |||||||
|                     <CRow> |                     <CRow> | ||||||
|                       <CCol> |                       <CCol> | ||||||
|                         <CAlert show={hadError} color="danger"> |                         <CAlert show={hadError} color="danger"> | ||||||
|                           {loginErrorText} |                           {t('login.login_error')} | ||||||
|                         </CAlert> |                         </CAlert> | ||||||
|                       </CCol> |                       </CCol> | ||||||
|                     </CRow> |                     </CRow> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user