From 1582331e898a94eeaa563882e5a412ffe02658cf Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 12 Aug 2024 14:48:52 +0530 Subject: [PATCH 1/2] improving dashboard speed --- .../components/layouts/header/Headers.js | 104 +++++++----------- .../pages/settings/integrations/AktoGPT.jsx | 30 +++-- .../web/polaris_web/web/src/apps/main/App.js | 3 + .../web/src/apps/main/PollingProvider.jsx | 57 ++++++++++ .../web/src/apps/signup/components/SignUp.jsx | 6 + 5 files changed, 122 insertions(+), 78 deletions(-) create mode 100644 apps/dashboard/web/polaris_web/web/src/apps/main/PollingProvider.jsx diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js index a8fa6dc8af..5421e9b518 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/layouts/header/Headers.js @@ -1,6 +1,6 @@ import { TopBar, Icon, Text, ActionList, Modal, TextField, HorizontalStack, Box, Avatar, VerticalStack, Button } from '@shopify/polaris'; import { NotificationMajor, CustomerPlusMajor, LogOutMinor, NoteMinor, ResourcesMajor, UpdateInventoryMajor, PageMajor, DynamicSourceMajor } from '@shopify/polaris-icons'; -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import Store from '../../../store'; import PersistStore from '../../../../main/PersistStore'; @@ -8,22 +8,27 @@ import './Headers.css' import api from '../../../../signup/api'; import func from '@/util/func'; import SemiCircleProgress from '../../shared/SemiCircleProgress'; -import testingApi from "../../../pages/testing/api" -import TestingStore from '../../../pages/testing/testingStore'; +import { usePolling } from '../../../../main/PollingProvider'; +import { debounce } from 'lodash'; + +function ContentWithIcon({icon,text, isAvatar= false}) { + return( + + + {isAvatar ?
: + } +
+ {text} +
+ ) +} export default function Header() { const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); - const [isSearchActive, setIsSearchActive] = useState(false); const [searchValue, setSearchValue] = useState(''); const [newAccount, setNewAccount] = useState('') const [showCreateAccount, setShowCreateAccount] = useState(false) - const [currentTestsObj, setCurrentTestsObj] = useState({ - totalTestsCompleted:0, - totalTestsInitiated:0, - totalTestsQueued: 0, - testRunsArr: [], - }) - + const { currentTestsObj, clearPollingInterval } = usePolling(); const navigate = useNavigate() const username = Store((state) => state.username) @@ -34,18 +39,15 @@ export default function Header() { const allRoutes = Store((state) => state.allRoutes) const allCollections = PersistStore((state) => state.allCollections) - const searchItemsArr = func.getSearchItemsArr(allRoutes, allCollections) - - const setCurrentTestingRuns = TestingStore(state => state.setCurrentTestingRuns) - const [intervalId, setIntervalId] = useState(null); - + const searchItemsArr = useMemo(() => func.getSearchItemsArr(allRoutes, allCollections), []) + const [filteredItemsArr, setFilteredItemsArr] = useState(searchItemsArr) const toggleIsUserMenuOpen = useCallback( () => setIsUserMenuOpen((isUserMenuOpen) => !isUserMenuOpen), [], ); const handleLogOut = async () => { - clearInterval(intervalId) + clearPollingInterval() api.logout().then(res => { resetAll(); storeAccessToken(null) @@ -59,7 +61,15 @@ export default function Header() { }) } - + const debouncedSearch = debounce((searchQuery) => { + if(searchQuery.length === 0){ + setFilteredItemsArr(searchItemsArr) + }else{ + const resultArr = searchItemsArr.filter((x) => x.content.toLowerCase().includes(searchQuery)) + setFilteredItemsArr(resultArr) + } + }, 500); + const accountsItems = Object.keys(accounts).map(accountId => { return { id: accountId, @@ -81,18 +91,6 @@ export default function Header() { window.location.href="/dashboard/onboarding" }) } - - function ContentWithIcon({icon,text, isAvatar= false}) { - return( - - - {isAvatar ?
: - } -
- {text} -
- ) - } const userMenuMarkup = ( ); - const handleSearchResultsDismiss = useCallback(() => { - setIsSearchActive(false); - setSearchValue(''); - }, []); - const handleSearchChange = useCallback((value) => { setSearchValue(value); - setIsSearchActive(value.length > 0); + debouncedSearch(value.toLowerCase()) }, []); const handleNavigateSearch = (url) => { navigate(url) - handleSearchResultsDismiss() + handleSearchChange('') } - const searchItems = searchItemsArr.map((item) => { + const searchItems = filteredItemsArr.slice(0,20).map((item) => { const icon = item.type === 'page' ? PageMajor : DynamicSourceMajor; return { value: item.content, @@ -150,7 +143,7 @@ export default function Header() { const searchResultsMarkup = ( x.value.toLowerCase().includes(searchValue.toLowerCase()))} + items={searchItems} /> ); @@ -171,7 +164,10 @@ export default function Header() { navigate(navUrl) } - const progress = currentTestsObj.totalTestsInitiated === 0 ? 0 : Math.floor((currentTestsObj.totalTestsCompleted * 100)/ currentTestsObj.totalTestsInitiated) + const progress = useMemo(() => { + return currentTestsObj.totalTestsInitiated === 0 ? 0 : Math.floor((currentTestsObj.totalTestsCompleted * 100) / currentTestsObj.totalTestsInitiated); + }, [currentTestsObj.totalTestsCompleted, currentTestsObj.totalTestsInitiated]); + const secondaryMenuMarkup = ( @@ -203,9 +199,9 @@ export default function Header() { showNavigationToggle userMenu={userMenuMarkup} searchField={searchFieldMarkup} - searchResultsVisible={isSearchActive} + searchResultsVisible={searchValue.length > 0} searchResults={searchResultsMarkup} - onSearchResultsDismiss={handleSearchResultsDismiss} + onSearchResultsDismiss={() =>handleSearchChange('')} secondaryMenu={secondaryMenuMarkup} /> ); - useEffect(() => { - const fetchTestingStatus = () => { - const id = setInterval(() => { - testingApi.fetchTestingRunStatus().then((resp) => { - setCurrentTestingRuns(resp.currentRunningTestsStatus); - setCurrentTestsObj({ - totalTestsInitiated: resp?.testRunsScheduled || 0, - totalTestsCompleted: resp?.totalTestsCompleted || 0, - totalTestsQueued: resp?.testRunsQueued || 0, - testRunsArr: resp?.currentRunningTestsStatus || [] - }); - }); - }, 2000); - setIntervalId(id); - }; - - fetchTestingStatus(); - - return () => { - clearInterval(intervalId); - }; - }, []); - - return ( topBarMarkup ); diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx index 4d6ccdea27..f42f1768c9 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx @@ -6,6 +6,7 @@ import "../settings.css" import settingFunctions from '../module' import func from "@/util/func" import PersistStore from '../../../../main/PersistStore' +import { debounce } from 'lodash' function AktoGPT() { @@ -23,7 +24,7 @@ function AktoGPT() { } useEffect(()=> { - setDisplayItems(apiCollections) + setDisplayItems(apiCollections.slice(0,30)) },[apiCollections]) useEffect(()=>{ @@ -69,7 +70,7 @@ function AktoGPT() { arr.reverse() } setSortOrder(!sortOrder) - setDisplayItems(arr) + setDisplayItems(arr.slice(0,30)) setTimeout(() => { setSelectedItems(clonedItems); }, 0) @@ -78,18 +79,23 @@ function AktoGPT() { ) + const debouncedSearch = debounce((searchQuery) => { + let localVar = selectedItems + setSelectedItems([]) + if(searchQuery.length === 0){ + setDisplayItems(apiCollections.slice(0,30)) + }else{ + const resultArr = displayItems.filter((x) => x?.displayName.toLowerCase().includes(searchQuery)) + setDisplayItems(resultArr.slice(0,30)) + setTimeout(() => { + setSelectedItems(localVar) + },0) + } + }, 500); + const searchResult = (item) =>{ setSearchValue(item) - let localVar = selectedItems; - setSelectedItems([]) - const filterRegex = new RegExp(item, 'i'); - const resultOptions = apiCollections.filter((option) => - option.displayName.match(filterRegex) - ); - setDisplayItems(resultOptions) - setTimeout(() => { - setSelectedItems(localVar); - }, 0) + debouncedSearch(item) } const SearchIcon = ( diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/App.js b/apps/dashboard/web/polaris_web/web/src/apps/main/App.js index 8b7ed7ebdf..19413f5ff3 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/main/App.js +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/App.js @@ -68,6 +68,7 @@ import PageBusinessEmail from "../signup/pages/PageBusinessEmail" import TokenValidator from "./TokenValidator" import { TableContextProvider } from "@/apps/dashboard/components/tables/TableContext"; import VulnerabilityReport from "../dashboard/pages/testing/vulnerability_report/VulnerabilityReport"; +import { PollingProvider } from "./PollingProvider"; // if you add a component in a new path, please verify the search implementation in function -> 'getSearchItemsArr' in func.js @@ -360,9 +361,11 @@ function App() { }, []) return ( + + ); } diff --git a/apps/dashboard/web/polaris_web/web/src/apps/main/PollingProvider.jsx b/apps/dashboard/web/polaris_web/web/src/apps/main/PollingProvider.jsx new file mode 100644 index 0000000000..ac7c574719 --- /dev/null +++ b/apps/dashboard/web/polaris_web/web/src/apps/main/PollingProvider.jsx @@ -0,0 +1,57 @@ +import React, { createContext, useContext, useEffect, useState, useRef } from 'react'; +import testingApi from "../dashboard/pages/testing/api" +import TestingStore from '../dashboard/pages/testing/testingStore'; +const PollingContext = createContext(); + +export const usePolling = () => useContext(PollingContext); + +export const PollingProvider = ({ children }) => { + const [currentTestsObj, setCurrentTestsObj] = useState({ + totalTestsCompleted: 0, + totalTestsInitiated: 0, + totalTestsQueued: 0, + testRunsArr: [], + }); + + const intervalIdRef = useRef(null); + const setCurrentTestingRuns = TestingStore(state => state.setCurrentTestingRuns) + + useEffect(() => { + const fetchTestingStatus = () => { + const id = setInterval(() => { + testingApi.fetchTestingRunStatus().then((resp) => { + setCurrentTestingRuns(resp?.currentRunningTestsStatus) + setCurrentTestsObj(prevState => { + const newTestsObj = { + totalTestsInitiated: resp?.testRunsScheduled || 0, + totalTestsCompleted: resp?.totalTestsCompleted || 0, + totalTestsQueued: resp?.testRunsQueued || 0, + testRunsArr: resp?.currentRunningTestsStatus || [] + }; + if (JSON.stringify(prevState) !== JSON.stringify(newTestsObj)) { + return newTestsObj; + } + return prevState; + }); + }); + }, 2000); + intervalIdRef.current = id; + }; + + fetchTestingStatus(); + + return () => { + clearInterval(intervalIdRef.current); + }; + }, []); + + const clearPollingInterval = () => { + clearInterval(intervalIdRef.current); + }; + + return ( + + {children} + + ); +}; diff --git a/apps/dashboard/web/polaris_web/web/src/apps/signup/components/SignUp.jsx b/apps/dashboard/web/polaris_web/web/src/apps/signup/components/SignUp.jsx index 5dfc578e7b..1d974b8da9 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/signup/components/SignUp.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/signup/components/SignUp.jsx @@ -7,6 +7,8 @@ import api from '../api' import func from '@/util/func' import "../styles.css" import Store from '../../dashboard/store' +import PersistStore from '../../main/PersistStore' +import { usePolling } from '../../main/PollingProvider' function SignUp() { @@ -22,6 +24,8 @@ function SignUp() { const azureUrl = window.AZURE_REQUEST_URL const githubId = window.GITHUB_CLIENT_ID const githubUrl = window.GITHUB_URL ? window.GITHUB_URL : "https://github.com" + const resetAll = PersistStore(state => state.resetAll) + const { clearPollingInterval } = usePolling(); const githubAuthObj = { logo: '/public/github_icon.svg', @@ -42,6 +46,8 @@ function SignUp() { } useEffect(() => { + resetAll() + clearPollingInterval() let copySsoList = [] if (githubId !== undefined && githubId.length > 0) { copySsoList.push(githubAuthObj) From d3ea87f4e6818d0745f903aec13977dc836b8ece Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 12 Aug 2024 14:54:17 +0530 Subject: [PATCH 2/2] fixing search for all collections --- .../apps/dashboard/pages/settings/integrations/AktoGPT.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx index f42f1768c9..798c41ee4f 100644 --- a/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx +++ b/apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/settings/integrations/AktoGPT.jsx @@ -59,7 +59,7 @@ function AktoGPT() { const sortItems = () =>{ setSelectedItems([]) - const arr = [...displayItems].sort((a, b) => { + const arr = [...apiCollections].sort((a, b) => { let aComp = a.displayName.replace(/[^a-zA-Z]/g, '').toLowerCase(); let bComp = b.displayName.replace(/[^a-zA-Z]/g, '').toLowerCase(); if (aComp < bComp) return -1; @@ -85,7 +85,7 @@ function AktoGPT() { if(searchQuery.length === 0){ setDisplayItems(apiCollections.slice(0,30)) }else{ - const resultArr = displayItems.filter((x) => x?.displayName.toLowerCase().includes(searchQuery)) + const resultArr = apiCollections.filter((x) => x?.displayName.toLowerCase().includes(searchQuery)) setDisplayItems(resultArr.slice(0,30)) setTimeout(() => { setSelectedItems(localVar)