From 25e4f9e92bb9dd26ca60ba1b32ca6018cdf167a4 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 12 Dec 2023 17:13:55 +0300 Subject: [PATCH 01/65] Organisation switch in settings page --- platform/src/pages/settings/index.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/src/pages/settings/index.jsx b/platform/src/pages/settings/index.jsx index 33d601c39b..0482015b98 100644 --- a/platform/src/pages/settings/index.jsx +++ b/platform/src/pages/settings/index.jsx @@ -4,6 +4,7 @@ import Tab from '@/components/Tabs/Tab'; import Password from './Tabs/Password'; import withAuth from '@/core/utils/protectedRoute'; import Team from './Tabs/Team'; +import { useSelector } from 'react-redux'; import { useEffect, useState } from 'react'; import { getAssignedGroupMembers } from '@/core/apis/Account'; import Profile from './Tabs/Profile'; @@ -21,6 +22,8 @@ const Settings = () => { const [loading, setLoading] = useState(false); const [userPermissions, setUserPermissions] = useState([]); const [userGroup, setUserGroup] = useState({}); + const preferences = useSelector((state) => state.defaults.individual_preferences); + const userInfo = useSelector((state) => state.login.userInfo); useEffect(() => { setLoading(true); @@ -34,6 +37,8 @@ const Settings = () => { if (storedUserPermissions && storedUserPermissions.length > 0) { setUserPermissions(storedUserPermissions); + } else { + setUserPermissions([]); } try { @@ -49,7 +54,7 @@ const Settings = () => { } finally { setLoading(false); } - }, []); + }, [userInfo, preferences]); return ( From 4bd180b98a0aeadf5b0dcc904b0e04f1f99e7f67 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 12 Dec 2023 17:33:07 +0300 Subject: [PATCH 02/65] Update index.jsx --- platform/src/pages/settings/index.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/src/pages/settings/index.jsx b/platform/src/pages/settings/index.jsx index 0482015b98..6cca841e53 100644 --- a/platform/src/pages/settings/index.jsx +++ b/platform/src/pages/settings/index.jsx @@ -4,12 +4,13 @@ import Tab from '@/components/Tabs/Tab'; import Password from './Tabs/Password'; import withAuth from '@/core/utils/protectedRoute'; import Team from './Tabs/Team'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import { useEffect, useState } from 'react'; import { getAssignedGroupMembers } from '@/core/apis/Account'; import Profile from './Tabs/Profile'; import OrganizationProfile from './Tabs/OrganizationProfile'; import { isEmpty } from 'underscore'; +import { setChartTab } from '@/lib/store/services/charts/ChartSlice'; const checkAccess = (requiredPermission, rolePermissions) => { const permissions = rolePermissions && rolePermissions.map((item) => item.permission); @@ -18,6 +19,7 @@ const checkAccess = (requiredPermission, rolePermissions) => { }; const Settings = () => { + const dispatch = useDispatch(); const [teamMembers, setTeamMembers] = useState([]); const [loading, setLoading] = useState(false); const [userPermissions, setUserPermissions] = useState([]); @@ -39,6 +41,7 @@ const Settings = () => { setUserPermissions(storedUserPermissions); } else { setUserPermissions([]); + dispatch(setChartTab(0)); } try { From 888c4f70251e54bcd8643cb515c692830a0c49d6 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Fri, 15 Dec 2023 04:53:59 +0300 Subject: [PATCH 03/65] fixed map responsiveness --- netmanager/src/assets/css/overlay-map.css | 41 +++++ netmanager/src/index.css | 19 +- .../src/views/components/MapIcons/Offline.js | 22 +++ .../Dashboard/components/Map/Indicator .js | 8 + .../src/views/pages/Heatmap/HeatMapOverlay.js | 167 +++++++----------- netmanager/src/views/pages/Map/OverlayMap.js | 164 +++++++---------- 6 files changed, 219 insertions(+), 202 deletions(-) create mode 100644 netmanager/src/views/components/MapIcons/Offline.js diff --git a/netmanager/src/assets/css/overlay-map.css b/netmanager/src/assets/css/overlay-map.css index e46692ab07..3967edbe4b 100644 --- a/netmanager/src/assets/css/overlay-map.css +++ b/netmanager/src/assets/css/overlay-map.css @@ -1,10 +1,31 @@ .overlay-map-container { + width: 100%; height: calc(100vh - 64px); height: -moz-calc(100vh - 64px); height: -webkit-calc(100vh - 64px); position: relative; } +.map-new-container { + width: 100%; + height: 100%; + position: relative; +} + +.map-circular-loader { + position: absolute; + z-index: 120; + left: 0; + top: 0; + width: 100%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(1px); +} + .map-wrapper { display: flex; flex-wrap: wrap; @@ -404,3 +425,23 @@ opacity: 0; transition: visibility 0s, opacity 0.2s ease; } + +/* tablet */ +@media (max-width: 1279px) { + .overlay-map-container { + width: 100vw; + height: calc(100vh - 64px); + } +} + +/* mobile */ +@media (max-width: 768px) { + .overlay-map-container { + width: 100vw; + height: calc(100vh - 64px); + } + + .air-quality-description { + display: none; + } +} diff --git a/netmanager/src/index.css b/netmanager/src/index.css index 73d9a4dadb..c8a9905d31 100644 --- a/netmanager/src/index.css +++ b/netmanager/src/index.css @@ -141,7 +141,7 @@ select { } .filter { - z-index: 1000; + z-index: 100; background-color: rgba(255, 255, 255, 1); position: absolute; top: 30px; @@ -343,10 +343,9 @@ label { .indicator { position: absolute; - /* center position */ left: 50%; transform: translateX(-50%); - z-index: 1000; + z-index: 100; bottom: 10px; display: flex; flex-wrap: wrap; @@ -386,6 +385,10 @@ label { .indicator .hazardous { background-color: #81202e; +} + +.indicator .Offline { + background-color: #868686; border-bottom-right-radius: 5px; border-top-right-radius: 5px; } @@ -402,12 +405,13 @@ label { margin: 0; } -@media (max-width: 868px) { +/* tablet */ +@media (max-width: 1024px) { .indicator { position: absolute; left: 34%; transform: translateX(-50%); - z-index: 1000; + z-index: 100; bottom: 10px; display: flex; flex-wrap: wrap; @@ -415,11 +419,14 @@ label { align-items: center; bottom: 43px; } +} + +@media (max-width: 868px) { .indicator .good { border-bottom-left-radius: 0; border-top-left-radius: 0; } - .indicator .hazardous { + .indicator .Offline { border-bottom-right-radius: 0; border-top-right-radius: 0; } diff --git a/netmanager/src/views/components/MapIcons/Offline.js b/netmanager/src/views/components/MapIcons/Offline.js new file mode 100644 index 0000000000..2814829ba6 --- /dev/null +++ b/netmanager/src/views/components/MapIcons/Offline.js @@ -0,0 +1,22 @@ +import React from 'react'; + +const Offline = ({ width, height, fill }) => { + return ( + + + + + + + ); +}; + +export default Offline; diff --git a/netmanager/src/views/pages/Dashboard/components/Map/Indicator .js b/netmanager/src/views/pages/Dashboard/components/Map/Indicator .js index 548b11ef46..663b9546eb 100644 --- a/netmanager/src/views/pages/Dashboard/components/Map/Indicator .js +++ b/netmanager/src/views/pages/Dashboard/components/Map/Indicator .js @@ -6,12 +6,14 @@ import Unhealthy from 'views/components/MapIcons/Unhealthy'; import UnhealthySensitive from 'views/components/MapIcons/UnhealthySen'; import VeryUnhealthy from 'views/components/MapIcons/VeryUnhealthy'; import Hazardous from 'views/components/MapIcons/Hazardous'; +import Offline from 'views/components/MapIcons/Offline'; const Tooltip = ({ children, text, icon, label }) => { const [showTooltip, setShowTooltip] = React.useState(false); return (
setShowTooltip(true)} onMouseLeave={() => setShowTooltip(false)}> @@ -107,6 +109,12 @@ const Indicator = () => { icon={}> Hazardous + }> + Offline +
)} diff --git a/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js b/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js index b9a3b81930..4bc5323b35 100644 --- a/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js +++ b/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js @@ -362,7 +362,6 @@ export const OverlayMap = ({ center, zoom, heatMapData, monitoringSiteData }) => const mapContainerRef = useRef(null); const [map, setMap] = useState(); const [showSensors, setShowSensors] = useState(true); - const [showHeatMap, setShowHeatMap] = useState(false); const [showCalibratedValues, setShowCalibratedValues] = useState(false); // Initialize showPollutant state with localStorage values @@ -395,7 +394,7 @@ export const OverlayMap = ({ center, zoom, heatMapData, monitoringSiteData }) => // Initialize map const map = new mapboxgl.Map({ container: mapContainerRef.current, - style: localStorage.getItem('mapStyle') || lightMapStyle, + style: localStorage.getItem('mapStyle') || streetMapStyle, center, zoom, maxZoom: 20 @@ -408,7 +407,7 @@ export const OverlayMap = ({ center, zoom, heatMapData, monitoringSiteData }) => setMap(map); // Clean up on unmount - return () => map.remove(); + // return () => map.remove(); }, []); // Update heatmap data when available @@ -488,86 +487,74 @@ export const OverlayMap = ({ center, zoom, heatMapData, monitoringSiteData }) => } }; + const createMarker = (feature) => { + const [seconds, duration] = getFirstDuration(feature.properties.time); + let pollutantValue = null; + let markerKey = ''; + + // Loop through the showPollutant object and get the value and key for the selected pollutant + for (const property in showPollutant) { + if (showPollutant[property]) { + markerKey = property; + pollutantValue = feature.properties[property] && feature.properties[property].value; + if (showCalibratedValues) { + pollutantValue = + feature.properties[property] && feature.properties[property].calibratedValue; + } + break; + } + } + + const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); + + const el = document.createElement('div'); + el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; + el.style.borderRadius = seconds >= MAX_OFFLINE_DURATION ? '10%' : '50%'; + el.style.display = 'flex'; + el.style.justifyContent = 'center'; + el.style.alignItems = 'center'; + el.style.fontSize = `${seconds >= MAX_OFFLINE_DURATION ? '10px' : '12px'}`; + el.style.width = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.height = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.padding = '10px'; + el.innerHTML = pollutantValue ? Math.floor(pollutantValue) : '--'; + + if ( + feature.geometry.coordinates.length >= 2 && + feature.geometry.coordinates[0] && + feature.geometry.coordinates[1] && + pollutantValue !== null + ) { + const marker = new mapboxgl.Marker(el) + .setLngLat(feature.geometry.coordinates) + .setPopup( + new mapboxgl.Popup({ + offset: 25, + className: 'map-popup' + }).setHTML(MapPopup(feature, showPollutant, pollutantValue, desc, duration, markerClass)) + ) + .addTo(map); + + // Listen to the zoom event of the map + map.on('zoom', function () { + // Get the current zoom level of the map + const zoom = map.getZoom(); + // Calculate the size based on the zoom level + const size = (30 * zoom) / 10; + // Set the size of the marker + el.style.width = `${size}px`; + el.style.height = `${size}px`; + }); + } + }; + return (
{showSensors && map && monitoringSiteData.features.length > 0 && monitoringSiteData.features.forEach((feature) => { - const [seconds, duration] = getFirstDuration(feature.properties.time); - let pollutantValue = - (showPollutant.pm2_5 && feature.properties.pm2_5 && feature.properties.pm2_5.value) || - (showPollutant.pm10 && feature.properties.pm10 && feature.properties.pm10.value) || - (showPollutant.no2 && feature.properties.no2 && feature.properties.no2.value) || - null; - - if (showCalibratedValues) { - pollutantValue = - (showPollutant.pm2_5 && - feature.properties.pm2_5 && - feature.properties.pm2_5.calibratedValue && - feature.properties.pm2_5.calibratedValue) || - (showPollutant.pm10 && - feature.properties.pm10 && - feature.properties.pm10.calibratedValue && - feature.properties.pm10.calibratedValue) || - (showPollutant.no2 && - feature.properties.no2 && - feature.properties.no2.calibratedValue && - feature.properties.no2.calibratedValue) || - null; - } - let markerKey = ''; - for (const property in showPollutant) { - if (showPollutant[property]) markerKey = property; - } - const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); - - const el = document.createElement('div'); - el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; - el.style.borderRadius = '50%'; - el.style.display = 'flex'; - el.style.justifyContent = 'center'; - el.style.alignItems = 'center'; - el.style.fontSize = '12px'; - el.style.width = '30px'; - el.style.height = '30px'; - el.style.padding = '10px'; - el.innerHTML = showPollutant.pm2_5 - ? Math.floor(feature.properties.pm2_5.value) - : showPollutant.pm10 - ? Math.floor(feature.properties.pm10.value) - : '--'; - - if ( - feature.geometry.coordinates.length >= 2 && - feature.geometry.coordinates[0] && - feature.geometry.coordinates[1] && - pollutantValue !== null - ) { - const marker = new mapboxgl.Marker(el) - .setLngLat(feature.geometry.coordinates) - .setPopup( - new mapboxgl.Popup({ - offset: 25, - className: 'map-popup' - }).setHTML( - MapPopup(feature, showPollutant, pollutantValue, desc, duration, markerClass) - ) - ) - .addTo(map); - - // Listen to the zoom event of the map - map.on('zoom', function () { - // Get the current zoom level of the map - const zoom = map.getZoom(); - // Calculate the size based on the zoom level - const size = (30 * zoom) / 10; - // Set the size of the marker - el.style.width = `${size}px`; - el.style.height = `${size}px`; - }); - } + createMarker(feature); })} @@ -599,37 +586,19 @@ const HeatMapOverlay = () => { if (isEmpty(monitoringSiteData.features)) { dispatch(loadMapEventsData()); } - }, [monitoringSiteData]); + }, [dispatch, monitoringSiteData.features.length]); return ( -
+
{monitoringSiteData && isEmpty(monitoringSiteData.features) && ( -
+
)} diff --git a/netmanager/src/views/pages/Map/OverlayMap.js b/netmanager/src/views/pages/Map/OverlayMap.js index 92a739c6f6..ff425025b6 100644 --- a/netmanager/src/views/pages/Map/OverlayMap.js +++ b/netmanager/src/views/pages/Map/OverlayMap.js @@ -379,7 +379,7 @@ export const OverlayMap = ({ center, zoom, monitoringSiteData }) => { useEffect(() => { const map = new mapboxgl.Map({ container: mapContainerRef.current, - style: localStorage.mapStyle ? localStorage.mapStyle : lightMapStyle, + style: localStorage.mapStyle ? localStorage.mapStyle : streetMapStyle, center, zoom, maxZoom: 20 @@ -410,86 +410,74 @@ export const OverlayMap = ({ center, zoom, monitoringSiteData }) => { } catch (err) {} }; + const createMarker = (feature) => { + const [seconds, duration] = getFirstDuration(feature.properties.time); + let pollutantValue = null; + let markerKey = ''; + + // Loop through the showPollutant object and get the value and key for the selected pollutant + for (const property in showPollutant) { + if (showPollutant[property]) { + markerKey = property; + pollutantValue = feature.properties[property] && feature.properties[property].value; + if (showCalibratedValues) { + pollutantValue = + feature.properties[property] && feature.properties[property].calibratedValue; + } + break; + } + } + + const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); + + const el = document.createElement('div'); + el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; + el.style.borderRadius = seconds >= MAX_OFFLINE_DURATION ? '10%' : '50%'; + el.style.display = 'flex'; + el.style.justifyContent = 'center'; + el.style.alignItems = 'center'; + el.style.fontSize = '12px'; + el.style.width = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.height = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.padding = '10px'; + el.innerHTML = pollutantValue ? Math.floor(pollutantValue) : '--'; + + if ( + feature.geometry.coordinates.length >= 2 && + feature.geometry.coordinates[0] && + feature.geometry.coordinates[1] && + pollutantValue !== null + ) { + const marker = new mapboxgl.Marker(el) + .setLngLat(feature.geometry.coordinates) + .setPopup( + new mapboxgl.Popup({ + offset: 25, + className: 'map-popup' + }).setHTML(MapPopup(feature, showPollutant, pollutantValue, desc, duration, markerClass)) + ) + .addTo(map); + + // Listen to the zoom event of the map + map.on('zoom', function () { + // Get the current zoom level of the map + const zoom = map.getZoom(); + // Calculate the size based on the zoom level + const size = (30 * zoom) / 10; + // Set the size of the marker + el.style.width = `${size}px`; + el.style.height = `${size}px`; + }); + } + }; + return (
{showSensors && map && monitoringSiteData.features.length > 0 && monitoringSiteData.features.forEach((feature) => { - const [seconds, duration] = getFirstDuration(feature.properties.time); - let pollutantValue = - (showPollutant.pm2_5 && feature.properties.pm2_5 && feature.properties.pm2_5.value) || - (showPollutant.pm10 && feature.properties.pm10 && feature.properties.pm10.value) || - (showPollutant.no2 && feature.properties.no2 && feature.properties.no2.value) || - null; - - if (showCalibratedValues) { - pollutantValue = - (showPollutant.pm2_5 && - feature.properties.pm2_5 && - feature.properties.pm2_5.calibratedValue && - feature.properties.pm2_5.calibratedValue) || - (showPollutant.pm10 && - feature.properties.pm10 && - feature.properties.pm10.calibratedValue && - feature.properties.pm10.calibratedValue) || - (showPollutant.no2 && - feature.properties.no2 && - feature.properties.no2.calibratedValue && - feature.properties.no2.calibratedValue) || - null; - } - let markerKey = ''; - for (const property in showPollutant) { - if (showPollutant[property]) markerKey = property; - } - const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); - - const el = document.createElement('div'); - el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; - el.style.borderRadius = '50%'; - el.style.display = 'flex'; - el.style.justifyContent = 'center'; - el.style.alignItems = 'center'; - el.style.fontSize = '12px'; - el.style.width = '30px'; - el.style.height = '30px'; - el.style.padding = '10px'; - el.innerHTML = showPollutant.pm2_5 - ? Math.floor(feature.properties.pm2_5.value) - : showPollutant.pm10 - ? Math.floor(feature.properties.pm10.value) - : '--'; - - if ( - feature.geometry.coordinates.length >= 2 && - feature.geometry.coordinates[0] && - feature.geometry.coordinates[1] && - pollutantValue !== null - ) { - const marker = new mapboxgl.Marker(el) - .setLngLat(feature.geometry.coordinates) - .setPopup( - new mapboxgl.Popup({ - offset: 25, - className: 'map-popup' - }).setHTML( - MapPopup(feature, showPollutant, pollutantValue, desc, duration, markerClass) - ) - ) - .addTo(map); - - // Listen to the zoom event of the map - map.on('zoom', function () { - // Get the current zoom level of the map - const zoom = map.getZoom(); - // Calculate the size based on the zoom level - const size = (30 * zoom) / 10; - // Set the size of the marker - el.style.width = `${size}px`; - el.style.height = `${size}px`; - }); - } + createMarker(feature); })} @@ -513,39 +501,21 @@ const MapContainer = () => { const monitoringSiteData = useEventsMapData(); useEffect(() => { - if (isEmpty(monitoringSiteData.features)) { + if (!monitoringSiteData.features.length) { dispatch(loadMapEventsData()); } - }, [monitoringSiteData]); + }, [dispatch, monitoringSiteData.features.length]); return ( -
+
{monitoringSiteData && isEmpty(monitoringSiteData.features) && ( -
+
)} From 7ed770ff52f42b3007e67610e08d721be54b0eef Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Fri, 15 Dec 2023 04:58:14 +0300 Subject: [PATCH 04/65] Update OverlayMap.js --- netmanager/src/views/pages/Map/OverlayMap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netmanager/src/views/pages/Map/OverlayMap.js b/netmanager/src/views/pages/Map/OverlayMap.js index ff425025b6..768a6e9332 100644 --- a/netmanager/src/views/pages/Map/OverlayMap.js +++ b/netmanager/src/views/pages/Map/OverlayMap.js @@ -511,7 +511,7 @@ const MapContainer = () => {
{monitoringSiteData && isEmpty(monitoringSiteData.features) && ( From 195a8535ed4b1fafd7a74936e0db4009ee6f1b24 Mon Sep 17 00:00:00 2001 From: Martin Bbaale Date: Sat, 16 Dec 2023 15:55:01 +0300 Subject: [PATCH 05/65] Update README.md for website Update README.md for website --- website/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/README.md b/website/README.md index 65e02d5036..969154adf5 100644 --- a/website/README.md +++ b/website/README.md @@ -26,7 +26,7 @@ #### OSX, Linux, Windows -- `Python 3.6 or higher (Python 3.7 preferred)` [Python Download](https://www.python.org/) +- `For Python 3.6 or higher (Python 3.7 preferred)` [Python Download](https://www.python.org/) - `NodeJs v12` [Node Download](https://nodejs.org/en/download/) - `Npm` [NpmJs](https://www.npmjs.com/get-npm) From 5999cb4ec49cae495a5a003dcf7e3efc70227024 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Sat, 16 Dec 2023 18:16:48 +0300 Subject: [PATCH 06/65] Created a maintenance page --- website/Maintenance/imgs/Logo.svg | 3 ++ website/Maintenance/imgs/Oops.svg | 20 +++++++++ website/Maintenance/maintenance.html | 62 ++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 website/Maintenance/imgs/Logo.svg create mode 100644 website/Maintenance/imgs/Oops.svg create mode 100644 website/Maintenance/maintenance.html diff --git a/website/Maintenance/imgs/Logo.svg b/website/Maintenance/imgs/Logo.svg new file mode 100644 index 0000000000..4c5868414f --- /dev/null +++ b/website/Maintenance/imgs/Logo.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/website/Maintenance/imgs/Oops.svg b/website/Maintenance/imgs/Oops.svg new file mode 100644 index 0000000000..072e50dc00 --- /dev/null +++ b/website/Maintenance/imgs/Oops.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/website/Maintenance/maintenance.html b/website/Maintenance/maintenance.html new file mode 100644 index 0000000000..df80c8466a --- /dev/null +++ b/website/Maintenance/maintenance.html @@ -0,0 +1,62 @@ + + + + Maintenance Page + + + + + + + +
+ maintenance +
+ We're currently performing some maintenance. Please check back soon. +
+
+ + From db15a7ebc791223d43013bef9a7a3e5d740ff4a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 18:37:51 +0300 Subject: [PATCH 07/65] Update next platform staging image tag to stage-418bfe8b-1702740909 --- k8s/platform/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/platform/values-stage.yaml b/k8s/platform/values-stage.yaml index 05c6156b25..5c55b1a18a 100644 --- a/k8s/platform/values-stage.yaml +++ b/k8s/platform/values-stage.yaml @@ -2,7 +2,7 @@ replicaCount: 1 image: repository: eu.gcr.io/airqo-250220/airqo-stage-next-platform pullPolicy: Always - tag: stage-dfe786a8-1702302133 + tag: stage-418bfe8b-1702740909 imagePullSecrets: [] nameOverride: '' fullnameOverride: '' From e7ef22ee898854e99a1b15147cbed4dc32ff71ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 18:41:47 +0300 Subject: [PATCH 08/65] Update analytics platform production image tag to prod-258c41e5-1702741144 --- k8s/platform/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/platform/values-prod.yaml b/k8s/platform/values-prod.yaml index c089a4bc3b..0798412868 100644 --- a/k8s/platform/values-prod.yaml +++ b/k8s/platform/values-prod.yaml @@ -2,7 +2,7 @@ replicaCount: 1 image: repository: eu.gcr.io/airqo-250220/airqo-next-platform pullPolicy: Always - tag: prod-8a8c7e6f-1702302188 + tag: prod-258c41e5-1702741144 imagePullSecrets: [] nameOverride: '' fullnameOverride: '' From d4eb6b7eccca1199d7594a87f1360e9bb1b0ec7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 18:47:52 +0300 Subject: [PATCH 09/65] Update netmanger staging image tag to stage-2279a5e8-1702741066 --- k8s/netmanager/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/netmanager/values-stage.yaml b/k8s/netmanager/values-stage.yaml index 4d5de4ff64..6f30122a00 100644 --- a/k8s/netmanager/values-stage.yaml +++ b/k8s/netmanager/values-stage.yaml @@ -1,7 +1,7 @@ replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-platform-frontend - tag: stage-ce577706-1702298410 + tag: stage-2279a5e8-1702741066 pullPolicy: Always imagePullSecrets: [] nameOverride: '' From b274c39676c1e16f5c2d56f8d98f27aaadfa0e4c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 18:49:16 +0300 Subject: [PATCH 10/65] Update netmanger production image tag to prod-258c41e5-1702741144 --- k8s/netmanager/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/netmanager/values-prod.yaml b/k8s/netmanager/values-prod.yaml index 3a9d36a135..70d6af204f 100644 --- a/k8s/netmanager/values-prod.yaml +++ b/k8s/netmanager/values-prod.yaml @@ -1,7 +1,7 @@ replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-platform-frontend - tag: prod-c144865b-1702298458 + tag: prod-258c41e5-1702741144 pullPolicy: Always imagePullSecrets: [] nameOverride: '' From 9e9fbeac61684c7d7c47a98d857f20439c6fe687 Mon Sep 17 00:00:00 2001 From: Martin Bbaale Date: Sun, 17 Dec 2023 13:13:15 +0300 Subject: [PATCH 11/65] WEBSITE >> Update README.md WEBSITE >> Update README.md --- website/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/README.md b/website/README.md index 969154adf5..381c025a9d 100644 --- a/website/README.md +++ b/website/README.md @@ -50,7 +50,7 @@ Change directory into the `website` folder of the cloned `AirQo-frontend` folder #### HomeBrew -Install homebrew +Now Install homebrew /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" From 82ea72494b37a73886c057bb9346beb3c34bc741 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Sun, 17 Dec 2023 14:57:01 +0300 Subject: [PATCH 12/65] removed gray node on the map --- netmanager/src/views/pages/Heatmap/HeatMapOverlay.js | 11 ++++++----- netmanager/src/views/pages/Map/OverlayMap.js | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js b/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js index 4bc5323b35..96c3930c31 100644 --- a/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js +++ b/netmanager/src/views/pages/Heatmap/HeatMapOverlay.js @@ -508,14 +508,15 @@ export const OverlayMap = ({ center, zoom, heatMapData, monitoringSiteData }) => const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); const el = document.createElement('div'); - el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; - el.style.borderRadius = seconds >= MAX_OFFLINE_DURATION ? '10%' : '50%'; + // el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; + el.className = `marker ${markerClass}`; + el.style.borderRadius = '50%'; el.style.display = 'flex'; el.style.justifyContent = 'center'; el.style.alignItems = 'center'; - el.style.fontSize = `${seconds >= MAX_OFFLINE_DURATION ? '10px' : '12px'}`; - el.style.width = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; - el.style.height = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.fontSize = '12px'; + el.style.width = '30px'; + el.style.height = '30px'; el.style.padding = '10px'; el.innerHTML = pollutantValue ? Math.floor(pollutantValue) : '--'; diff --git a/netmanager/src/views/pages/Map/OverlayMap.js b/netmanager/src/views/pages/Map/OverlayMap.js index 768a6e9332..df994bab3a 100644 --- a/netmanager/src/views/pages/Map/OverlayMap.js +++ b/netmanager/src/views/pages/Map/OverlayMap.js @@ -431,14 +431,15 @@ export const OverlayMap = ({ center, zoom, monitoringSiteData }) => { const [markerClass, desc] = getMarkerDetail(pollutantValue, markerKey); const el = document.createElement('div'); - el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; - el.style.borderRadius = seconds >= MAX_OFFLINE_DURATION ? '10%' : '50%'; + // el.className = `marker ${seconds >= MAX_OFFLINE_DURATION ? 'marker-grey' : markerClass}`; + el.className = `marker ${markerClass}`; + el.style.borderRadius = '50%'; el.style.display = 'flex'; el.style.justifyContent = 'center'; el.style.alignItems = 'center'; el.style.fontSize = '12px'; - el.style.width = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; - el.style.height = seconds >= MAX_OFFLINE_DURATION ? '15px' : '30px'; + el.style.width = '30px'; + el.style.height = '30px'; el.style.padding = '10px'; el.innerHTML = pollutantValue ? Math.floor(pollutantValue) : '--'; From 484a1949f421bf6c9e27acf8d9faa3a62498463e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 15:11:16 +0300 Subject: [PATCH 13/65] Update netmanger staging image tag to stage-8c666611-1702814406 --- k8s/netmanager/values-stage.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/netmanager/values-stage.yaml b/k8s/netmanager/values-stage.yaml index 6f30122a00..a607cc6ce2 100644 --- a/k8s/netmanager/values-stage.yaml +++ b/k8s/netmanager/values-stage.yaml @@ -1,7 +1,7 @@ replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-stage-platform-frontend - tag: stage-2279a5e8-1702741066 + tag: stage-8c666611-1702814406 pullPolicy: Always imagePullSecrets: [] nameOverride: '' From f1226d445bd78cb2ac6f0ab63096a95fa718f63b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 15:11:56 +0300 Subject: [PATCH 14/65] Update netmanger production image tag to prod-3ad7440c-1702814516 --- k8s/netmanager/values-prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/netmanager/values-prod.yaml b/k8s/netmanager/values-prod.yaml index 70d6af204f..0cd06cd8e8 100644 --- a/k8s/netmanager/values-prod.yaml +++ b/k8s/netmanager/values-prod.yaml @@ -1,7 +1,7 @@ replicaCount: 2 image: repository: eu.gcr.io/airqo-250220/airqo-platform-frontend - tag: prod-258c41e5-1702741144 + tag: prod-3ad7440c-1702814516 pullPolicy: Always imagePullSecrets: [] nameOverride: '' From b382332171d25b82369b15b6d0838d75284bf332 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Sun, 17 Dec 2023 23:01:56 +0300 Subject: [PATCH 15/65] Update README.md --- website/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/README.md b/website/README.md index 381c025a9d..d85d5be3b7 100644 --- a/website/README.md +++ b/website/README.md @@ -1,6 +1,6 @@ # The AirQo Website. ---- +## - [Prerequisites](#prerequisites) - [OSX, Linux, Windows](#osx-linux-windows) From ff4dd017f092548450ad37b03a881c1416569b6c Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Sun, 17 Dec 2023 23:13:59 +0300 Subject: [PATCH 16/65] changed file in django backend --- website/README.md | 2 +- ...scription_alter_career_options_and_more.py | 92 ++++++++++++------- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/website/README.md b/website/README.md index d85d5be3b7..3c1fc19057 100644 --- a/website/README.md +++ b/website/README.md @@ -1,6 +1,6 @@ # The AirQo Website. -## +## - [Prerequisites](#prerequisites) - [OSX, Linux, Windows](#osx-linux-windows) diff --git a/website/backend/career/migrations/0002_bulletdescription_alter_career_options_and_more.py b/website/backend/career/migrations/0002_bulletdescription_alter_career_options_and_more.py index 1967f0d89c..915148ca9b 100644 --- a/website/backend/career/migrations/0002_bulletdescription_alter_career_options_and_more.py +++ b/website/backend/career/migrations/0002_bulletdescription_alter_career_options_and_more.py @@ -4,7 +4,7 @@ from django.conf import settings from django.db import migrations, models import django.db.models.deletion -from django.utils.timezone import utc +from datetime import timezone import django.utils.timezone import django_extensions.db.fields @@ -20,12 +20,16 @@ class Migration(migrations.Migration): migrations.CreateModel( name='BulletDescription', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), - ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified')), ('name', models.CharField(max_length=30)), ('order', models.IntegerField(default=1)), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bulletdescription_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='bulletdescription_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), ], options={ 'get_latest_by': 'modified', @@ -56,46 +60,58 @@ class Migration(migrations.Migration): migrations.AddField( model_name='career', name='apply_url', - field=models.URLField(default=datetime.datetime(2022, 7, 12, 20, 52, 5, 796544, tzinfo=utc), max_length=250), + field=models.URLField(default=datetime.datetime( + 2022, 7, 12, 20, 52, 5, 796544, tzinfo=timezone.utc), max_length=250), preserve_default=False, ), migrations.AddField( model_name='career', name='author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='career_create', to=settings.AUTH_USER_MODEL, verbose_name='author'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='career_create', to=settings.AUTH_USER_MODEL, verbose_name='author'), ), migrations.AddField( model_name='career', name='created', - field=django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='created'), + field=django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, default=django.utils.timezone.now, verbose_name='created'), preserve_default=False, ), migrations.AddField( model_name='career', name='modified', - field=django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified'), + field=django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified'), ), migrations.AddField( model_name='career', name='type', - field=models.CharField(choices=[('full-time', 'Full Time'), ('part-time', 'Part Time'), ('contract', 'Contract'), ('temporary', 'Temporary'), ('internship', 'Internship'), ('graduate-training', 'Graduate Training')], default='full-time', max_length=30), + field=models.CharField(choices=[('full-time', 'Full Time'), ('part-time', 'Part Time'), ('contract', 'Contract'), ('temporary', + 'Temporary'), ('internship', 'Internship'), ('graduate-training', 'Graduate Training')], default='full-time', max_length=30), ), migrations.AddField( model_name='career', name='updated_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='career_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='career_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by'), ), migrations.CreateModel( name='JobDescription', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), - ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified')), ('description', models.TextField()), ('order', models.IntegerField(default=1)), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='jobdescription_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('career', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='descriptions', to='career.career')), - ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='jobdescription_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), + ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='jobdescription_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('career', models.ForeignKey(blank=True, null=True, + on_delete=django.db.models.deletion.SET_NULL, related_name='descriptions', to='career.career')), + ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='jobdescription_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), ], options={ 'get_latest_by': 'modified', @@ -105,12 +121,17 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Department', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), - ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified')), ('name', models.CharField(max_length=30)), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='department_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='department_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), + ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='department_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='department_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), ], options={ 'get_latest_by': 'modified', @@ -120,14 +141,20 @@ class Migration(migrations.Migration): migrations.CreateModel( name='BulletPoint', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')), - ('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('created', django_extensions.db.fields.CreationDateTimeField( + auto_now_add=True, verbose_name='created')), + ('modified', django_extensions.db.fields.ModificationDateTimeField( + auto_now=True, verbose_name='modified')), ('description', models.TextField()), ('order', models.IntegerField(default=1)), - ('Bullet', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bullet_points', to='career.bulletdescription')), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bulletpoint_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bulletpoint_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), + ('Bullet', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='bullet_points', to='career.bulletdescription')), + ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='bulletpoint_create', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='bulletpoint_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by')), ], options={ 'get_latest_by': 'modified', @@ -137,16 +164,19 @@ class Migration(migrations.Migration): migrations.AddField( model_name='bulletdescription', name='career', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bullets', to='career.career'), + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bullets', to='career.career'), ), migrations.AddField( model_name='bulletdescription', name='updated_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bulletdescription_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='bulletdescription_update', to=settings.AUTH_USER_MODEL, verbose_name='last updated by'), ), migrations.AddField( model_name='career', name='department', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='careers', to='career.department'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, + related_name='careers', to='career.department'), ), ] From ce8f22d79ed7788abdea4d745c612cbb718ecaf1 Mon Sep 17 00:00:00 2001 From: Martin Bbaale Date: Fri, 22 Dec 2023 23:52:17 +0300 Subject: [PATCH 17/65] Update README.md Update README.md --- website/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/README.md b/website/README.md index 3c1fc19057..e1e780fc1b 100644 --- a/website/README.md +++ b/website/README.md @@ -26,7 +26,7 @@ #### OSX, Linux, Windows -- `For Python 3.6 or higher (Python 3.7 preferred)` [Python Download](https://www.python.org/) +- `Python 3.6 or higher (Python 3.7 preferred)` [Python Download](https://www.python.org/) - `NodeJs v12` [Node Download](https://nodejs.org/en/download/) - `Npm` [NpmJs](https://www.npmjs.com/get-npm) From 179cd27873a692e8b269fd5c91bf61d8ce6b0c6b Mon Sep 17 00:00:00 2001 From: Benjamin Ssempala <86492979+BenjaminSsempala@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:29:10 +0300 Subject: [PATCH 18/65] Automate Deletion of old versions AppEngine --- .github/workflows/deploy-frontend-pr-previews.yml | 10 ++++++++++ .github/workflows/deploy-frontends-to-production.yml | 9 +++++++++ .github/workflows/deploy-frontends-to-staging.yml | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/.github/workflows/deploy-frontend-pr-previews.yml b/.github/workflows/deploy-frontend-pr-previews.yml index 47cab38b93..c5c4607b63 100644 --- a/.github/workflows/deploy-frontend-pr-previews.yml +++ b/.github/workflows/deploy-frontend-pr-previews.yml @@ -565,6 +565,16 @@ jobs: --project=${{ secrets.FRONTEND_PROJECT_ID }} \ --quiet + - name: Delete Old Versions + run: |- + service="website-pr-previews" + versions=$(gcloud app versions list --service=$service --sort-by '~LAST_DEPLOYED' --format 'value(VERSION.ID)' | sort -r | tail -n +4) + for version in $versions; do + echo "Deleting version: $version for service: $service" + gcloud app versions delete "$version" --service=$service --quiet + done + + website-pr-comment: name: website-preview-link-comment if: needs.check.outputs.run_website == 'true' diff --git a/.github/workflows/deploy-frontends-to-production.yml b/.github/workflows/deploy-frontends-to-production.yml index 6ac0a590c3..4c4cfcd8e7 100644 --- a/.github/workflows/deploy-frontends-to-production.yml +++ b/.github/workflows/deploy-frontends-to-production.yml @@ -205,6 +205,15 @@ jobs: --image-url=${{ env.REGISTRY_URL }}/${{ secrets.FRONTEND_PROJECT_ID }}/airqo-website:latest \ --project=${{ secrets.FRONTEND_PROJECT_ID }} \ --quiet + + - name: Delete Old Versions + run: |- + service="default" + versions=$(gcloud app versions list --service=$service --sort-by '~LAST_DEPLOYED' --format 'value(VERSION.ID)' | sort -r | tail -n +4) + for version in $versions; do + echo "Deleting version: $version for service: $service" + gcloud app versions delete "$version" --service=$service --quiet + done ### build and push calibrate app image ### calibrate-app: diff --git a/.github/workflows/deploy-frontends-to-staging.yml b/.github/workflows/deploy-frontends-to-staging.yml index 632b418506..93af3a9f06 100644 --- a/.github/workflows/deploy-frontends-to-staging.yml +++ b/.github/workflows/deploy-frontends-to-staging.yml @@ -255,6 +255,16 @@ jobs: --project=${{ secrets.FRONTEND_PROJECT_ID }} \ --quiet + - name: Delete Old Versions + run: |- + service="staging" + versions=$(gcloud app versions list --service=$service --sort-by '~LAST_DEPLOYED' --format 'value(VERSION.ID)' | sort -r | tail -n +4) + for version in $versions; do + echo "Deleting version: $version for service: $service" + gcloud app versions delete "$version" --service=$service --quiet + done + + ### build and push calibrate app image ### calibrate-app: name: build-push-calibrate-app-image From 8ab5666fb5729657a3035a29123cc1ff5eb854ec Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Thu, 4 Jan 2024 20:52:28 +0300 Subject: [PATCH 19/65] worked on group invite UX improvements If an invited user already exists in the system, they will be redirected to the login page else they will continue with the registration process and their email address will be auto filled during account creation. --- .../pages/account/creation/individual/register.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/src/pages/account/creation/individual/register.jsx b/platform/src/pages/account/creation/individual/register.jsx index 822e0babcd..c04a5fc458 100644 --- a/platform/src/pages/account/creation/individual/register.jsx +++ b/platform/src/pages/account/creation/individual/register.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import AccountPageLayout from '@/components/Account/Layout'; import Link from 'next/link'; import { useDispatch } from 'react-redux'; @@ -28,12 +28,22 @@ const IndividualAccountRegistration = () => { const dispatch = useDispatch(); const router = useRouter(); + const { userExists, userEmail } = router.query; const [loading, setLoading] = useState(false); const [creationErrors, setCreationErrors] = useState({ state: false, message: '', }); + useEffect(() => { + if (userEmail) { + setEmail(userEmail); + } + if (userExists === 'true') { + router.push('/account/login'); + } + }, [userExists, userEmail]); + const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); @@ -148,6 +158,7 @@ const IndividualAccountRegistration = () => {
Email address*
setEmail(e.target.value)} type='email' placeholder='Enter your email' From 8486abe0943a055fe157a7d5a9e294a398296ba1 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Fri, 5 Jan 2024 15:23:25 +0300 Subject: [PATCH 20/65] UI/UX improvements Included breadcrumbs on sim and host registry views plus improved display of airqo name to AirQo on all views for consistency Improved candidates table to avoid congestion by use of a select element where users can now select the columns they wish to view with out over congesting the view Improved the view of active buttons in the user stats page --- .../views/components/DataDisplay/Devices.js | 9 +- .../src/views/components/Hosts/HostsTable.js | 6 + .../src/views/components/SIM/SimRegistry.js | 39 +- .../views/components/Sites/SiteRegistry.js | 5 +- .../src/views/components/Sites/SitesTable.js | 4 +- .../pages/CandidateList/CandidateList.js | 5 +- .../CandidatesTable/CandidatesTable.js | 724 ++++++++++-------- .../src/views/pages/UserStats/UserStats.js | 77 +- 8 files changed, 484 insertions(+), 385 deletions(-) diff --git a/netmanager/src/views/components/DataDisplay/Devices.js b/netmanager/src/views/components/DataDisplay/Devices.js index 74a008a64b..623fd87d4d 100644 --- a/netmanager/src/views/components/DataDisplay/Devices.js +++ b/netmanager/src/views/components/DataDisplay/Devices.js @@ -699,10 +699,15 @@ const DevicesTable = (props) => { {activeNetwork.net_name === 'airqo' ? 'Soft Add Device' : 'Add Device'}
- + Object.assign({}, x))} diff --git a/netmanager/src/views/components/Hosts/HostsTable.js b/netmanager/src/views/components/Hosts/HostsTable.js index 503c1e9ec0..4a8f6c5b59 100644 --- a/netmanager/src/views/components/Hosts/HostsTable.js +++ b/netmanager/src/views/components/Hosts/HostsTable.js @@ -25,6 +25,7 @@ import RemoveIcon from '@material-ui/icons/Remove'; import IconButton from '@material-ui/core/IconButton'; import AddIcon from '@material-ui/icons/Add'; import Tooltip from '@material-ui/core/Tooltip'; +import UsersListBreadCrumb from '../../pages/UserList/components/Breadcrumb'; const activeNetwork = JSON.parse(localStorage.getItem('activeNetwork')); @@ -380,6 +381,7 @@ const HostsTable = () => { const [isLoading, setIsLoading] = useState(false); const [addHostDialog, setAddHostDialog] = useState(false); const [refreshData, setRefreshData] = useState(false); + const activeNetwork = JSON.parse(localStorage.getItem('activeNetwork')); const getHosts = async () => { try { @@ -421,6 +423,10 @@ const HostsTable = () => { Add Host
+ ({ root: { @@ -65,39 +66,6 @@ const useStyles = makeStyles((theme) => ({ } })); -const customStyles = { - control: (base, state) => ({ - ...base, - height: '50px', - marginTop: '10px', - marginBottom: '10px', - borderColor: state.isFocused ? '#3f51b5' : '#9a9a9a', - '&:hover': { - borderColor: state.isFocused ? 'black' : 'black' - }, - boxShadow: state.isFocused ? '0 0 1px 1px #3f51b5' : null - }), - option: (provided, state) => ({ - ...provided, - borderBottom: '1px dotted pink', - color: state.isSelected ? 'white' : 'blue', - textAlign: 'left' - }), - input: (provided, state) => ({ - ...provided, - height: '40px', - borderColor: state.isFocused ? '#3f51b5' : 'black' - }), - placeholder: (provided, state) => ({ - ...provided, - color: '#000' - }), - menu: (provided, state) => ({ - ...provided, - zIndex: 9999 - }) -}; - const RegisterSim = ({ setCreateSimDialog, CreateSimDialog, setIsLoading, setRefresh }) => { const dispatch = useDispatch(); const classes = useStyles(); @@ -243,6 +211,7 @@ const SimRegistry = () => { const [refresh, setRefresh] = useState(false); const [CreateSimDialog, setCreateSimDialog] = useState(false); const [simData, setSimData] = useState([]); + const activeNetwork = JSON.parse(localStorage.getItem('activeNetwork')); useEffect(() => { setLoading(true); @@ -296,6 +265,10 @@ const SimRegistry = () => { Register SIM
+ {
- +
diff --git a/netmanager/src/views/components/Sites/SitesTable.js b/netmanager/src/views/components/Sites/SitesTable.js index e00bcc5169..096ee23992 100644 --- a/netmanager/src/views/components/Sites/SitesTable.js +++ b/netmanager/src/views/components/Sites/SitesTable.js @@ -86,7 +86,9 @@ const SitesTable = () => { { return (
- +
diff --git a/netmanager/src/views/pages/CandidateList/components/CandidatesTable/CandidatesTable.js b/netmanager/src/views/pages/CandidateList/components/CandidatesTable/CandidatesTable.js index 8e93d99c1d..877b014654 100644 --- a/netmanager/src/views/pages/CandidateList/components/CandidatesTable/CandidatesTable.js +++ b/netmanager/src/views/pages/CandidateList/components/CandidatesTable/CandidatesTable.js @@ -36,6 +36,9 @@ import { updateMainAlert } from 'redux/MainAlert/operations'; import { createAlertBarExtraContentFromObject } from 'utils/objectManipulators'; import CandidateDrawer from '../CandidateDrawer'; import { isEmpty } from 'underscore'; +import InputLabel from '@material-ui/core/InputLabel'; +import Select from 'react-select'; +import makeAnimated from 'react-select/animated'; const useStyles = makeStyles((theme) => ({ root: {}, @@ -57,10 +60,52 @@ const useStyles = makeStyles((theme) => ({ }, description: { width: '300px' + }, + formControl: { + margin: theme.spacing(1), + minWidth: 100, + maxWidth: 300 + }, + chips: { + display: 'flex', + flexWrap: 'wrap' + }, + chip: { + margin: 2 + }, + noLabel: { + marginTop: theme.spacing(3) } })); +const customStyles = { + control: (base, state) => ({ + ...base, + height: '45px', + width: '100%', + margin: '0 0 10px 0', + overflow: 'scroll' + }), + option: (provided, state) => ({ + ...provided, + borderBottom: '1px dotted pink', + color: state.isSelected ? 'white' : 'blue', + textAlign: 'left' + }), + input: (provided, state) => ({ + ...provided, + height: '40px', + marginBottom: '-10px', + borderColor: state.isFocused ? '#3f51b5' : 'black' + }), + menu: (provided, state) => ({ + ...provided, + zIndex: 9999 + }) +}; + const CandidatesTable = (props) => { + const animatedComponents = makeAnimated(); const { className, mappeduserState, ...rest } = props; const dispatch = useDispatch(); const [open, setOpen] = useState(false); @@ -265,335 +310,370 @@ const CandidatesTable = (props) => { } }; + const initialColumns = [ + { + title: 'Full Name', + render: (user) => { + return ( +
+ {user.firstName + ' ' + user.lastName} +
+ ); + } + }, + { + title: 'Email', + field: 'email' + }, + { + title: 'Country', + field: 'country' + }, + { + title: 'Category', + field: 'category' + }, + { + title: 'Description', + field: 'description', + render: (candidate) => { + let description = candidate.description; + + return ( + + + {description.length > 300 ? description.substring(0, 300) + '...' : description} + + {description.length > 128 ? ( + { + setShowFullDescription(!showFullDescription); + setDrawerCandidate(candidate); + }}> + {!showFullDescription && 'Show More'} + + ) : null} + + ); + } + }, + { + title: 'Status', + field: 'status', + render: (candidate) => { + const statusColor = { + pending: 'orange', + approved: 'green', + rejected: 'red' + }; + return ( + + {candidate.status} + + ); + } + }, + { + title: 'Organization', + field: 'long_organization' + }, + { + title: 'Job Title', + field: 'jobTitle' + }, + { + title: 'Submitted', + field: 'createdAt', + render: (candidate) => ( + {candidate.createdAt ? formatDateString(candidate.createdAt) : '---'} + ) + }, + { + title: 'Action', + render: (candidate) => ( +
+ + {candidate.status === 'rejected' ? ( + + ) : ( + + )} +
+ ) + }, + { + title: 'Message', + render: (candidate) => ( +
+ +
+ ) + } + ]; + + const initialColumnsSelected = [ + initialColumns[0], + initialColumns[1], + initialColumns[2], + initialColumns[3], + initialColumns[4], + initialColumns[5], + initialColumns[9] + ]; + const [selectedColumns, setSelectedColumns] = useState(initialColumnsSelected); + + const handleColumnSelect = (selectedOptions) => { + const selectedColumnTitles = selectedOptions.map((option) => option.value); + const selectedColumns = initialColumns.filter((col) => + selectedColumnTitles.includes(col.title) + ); + setSelectedColumns(selectedColumns); + }; + return ( - - { - return ( -
- - {getInitials(`${user.firstName + ' ' + user.lastName}`)} - - {user.firstName + ' ' + user.lastName} -
- ); - } - }, - { - title: 'Email', - field: 'email' - }, - { - title: 'Country', - field: 'country' - }, - { - title: 'Category', - field: 'category' - }, - { - title: 'Description', - field: 'description', - render: (candidate) => { - let description = candidate.description; - - return ( - - - {description.length > 300 ? description.substring(0, 300) + '...' : description} - - {description.length > 128 ? ( - { - setShowFullDescription(!showFullDescription); - setDrawerCandidate(candidate); - }} - > - {!showFullDescription && 'Show More'} - - ) : null} - - ); - } - }, - { - title: 'Status', - field: 'status', - render: (candidate) => { - const statusColor = { - pending: 'orange', - approved: 'green', - rejected: 'red' - }; - return ( - - {candidate.status} - - ); - } - }, - { - title: 'Organization', - field: 'long_organization' - }, - { - title: 'Job Title', - field: 'jobTitle' - }, - { - title: 'Submitted', - field: 'createdAt', - render: (candidate) => ( - {candidate.createdAt ? formatDateString(candidate.createdAt) : '---'} - ) - }, - { - title: 'Action', - render: (candidate) => ( + <> +
+ + Select Columns to display + +