Skip to content

Commit

Permalink
Merge pull request #1330 from akto-api-security/temp/dashboard_improv…
Browse files Browse the repository at this point in the history
…ement

Dashboard improvement
  • Loading branch information
avneesh-akto authored Aug 8, 2024
2 parents 65fa311 + 4d48199 commit c7026be
Showing 28 changed files with 266 additions and 320 deletions.
Original file line number Diff line number Diff line change
@@ -32,14 +32,13 @@ function CollectionComponent(props) {
}, [condition])

const allCollections = PersistStore(state => state.allCollections);
const allCollectionsOptions = allCollections.filter(x => x.type !== "API_GROUP")
const allCollectionsOptions = Object.keys(allCollections).filter(x => allCollections[x].type !== "API_GROUP")
.map(collection => {
return {
label: collection.displayName,
value: collection.id
label: allCollections[collection].displayName,
value: collection
}
})

const handleCollectionSelected = (collectionId) => {
dispatch({ type: "overwrite", index: index, key: "data", obj: { [collectionId]: [] } })
}
@@ -55,8 +54,6 @@ function CollectionComponent(props) {
return Object.keys(data)[0];
}

const mapCollectionIdToName = func.mapCollectionIdToName(allCollections)

const handleEndpointsSelected = (apiEndpoints, data) => {
let collectionId = getCollectionId(data);
if (collectionId) {
@@ -113,7 +110,7 @@ function CollectionComponent(props) {
optionsList={allCollectionsOptions}
setSelected={(collectionId) => handleCollectionSelected(collectionId)}
preSelected={[Number(getCollectionId(condition.data))]}
value={mapCollectionIdToName[getCollectionId(condition.data)]}
value={allCollections[getCollectionId(condition.data)]?.displayName}
/>
</div>
<div style={{ flex: "5" }}>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ButtonGroup, HorizontalStack, TextField } from '@shopify/polaris'
import { TextField } from '@shopify/polaris'
import React, { useEffect, useState } from 'react'
import Dropdown from './layouts/Dropdown';
import DropdownSearch from './shared/DropdownSearch';
@@ -14,10 +14,10 @@ function ConditionComponent(props) {
fetchApiEndpoints(condition)
},[condition])
const allCollections = PersistStore(state => state.allCollections);
const allCollectionsOptions = allCollections.map(collection => {
const allCollectionsOptions = Object.keys(allCollections).map(collection => {
return {
label: collection.displayName,
value: collection.id
label: allCollections[collection].displayName,
value: collection
}
})
const getApiEndpointsOptions = (data) => {
@@ -55,7 +55,6 @@ function ConditionComponent(props) {
})
}
}
const mapCollectionIdToName = func.mapCollectionIdToName(allCollections)
const [apiEndpoints, setApiEndpoints] = useState({})

const handleTextChange = (value) => {
@@ -112,7 +111,7 @@ function ConditionComponent(props) {
optionsList={allCollectionsOptions}
setSelected={(collectionId) => handleCollectionSelected(collectionId)}
preSelected={[Number(getCollectionId(field))]}
value={mapCollectionIdToName[getCollectionId(field)]}
value={allCollections[getCollectionId(field)]?.displayName}
/>
</div>
<div style={{flexGrow:"1"}}>
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
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';
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(
<HorizontalStack gap={2}>
<Box width='20px'>
{isAvatar ? <div className='reduce-size'><Avatar size="extraSmall" source={icon} /> </div>:
<Icon source={icon} color="base" />}
</Box>
<Text>{text}</Text>
</HorizontalStack>
)
}

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)
@@ -32,20 +37,24 @@ export default function Header() {
const activeAccount = Store(state => state.activeAccount)
const resetAll = PersistStore(state => state.resetAll)

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(),[])
const [filteredItemsArr, setFilteredItemsArr] = useState(searchItemsArr)

const toggleIsUserMenuOpen = useCallback(
() => setIsUserMenuOpen((isUserMenuOpen) => !isUserMenuOpen),
[],
);
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 handleLogOut = async () => {
clearInterval(intervalId)
clearPollingInterval()
api.logout().then(res => {
resetAll();
storeAccessToken(null)
@@ -58,8 +67,6 @@ export default function Header() {
navigate("/");
})
}


const accountsItems = Object.keys(accounts).map(accountId => {
return {
id: accountId,
@@ -81,18 +88,6 @@ export default function Header() {
window.location.href="/dashboard/onboarding"
})
}

function ContentWithIcon({icon,text, isAvatar= false}) {
return(
<HorizontalStack gap={2}>
<Box width='20px'>
{isAvatar ? <div className='reduce-size'><Avatar size="extraSmall" source={icon} /> </div>:
<Icon source={icon} color="base" />}
</Box>
<Text>{text}</Text>
</HorizontalStack>
)
}

const userMenuMarkup = (
<TopBar.UserMenu
@@ -124,23 +119,17 @@ export default function Header() {
/>
);

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 icon = item.type === 'page' ? PageMajor : DynamicSourceMajor;
const searchItems = filteredItemsArr.slice(0,20).map((item) => {
const icon = DynamicSourceMajor;
return {
value: item.content,
content: <ContentWithIcon text={item.content} icon={icon} />,
@@ -150,7 +139,7 @@ export default function Header() {

const searchResultsMarkup = (
<ActionList
items={searchItems.filter(x => x.value.toLowerCase().includes(searchValue.toLowerCase()))}
items={searchItems}
/>
);

@@ -171,7 +160,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 = (
<HorizontalStack gap={"4"}>
@@ -203,9 +195,9 @@ export default function Header() {
showNavigationToggle
userMenu={userMenuMarkup}
searchField={searchFieldMarkup}
searchResultsVisible={isSearchActive}
searchResultsVisible={searchValue.length > 0}
searchResults={searchResultsMarkup}
onSearchResultsDismiss={handleSearchResultsDismiss}
onSearchResultsDismiss={() => handleSearchChange('')}
secondaryMenu={secondaryMenuMarkup}
/>
<Modal
@@ -237,30 +229,6 @@ export default function Header() {
</div>
);

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
);
Original file line number Diff line number Diff line change
@@ -15,9 +15,9 @@ function ApiCollectionsDropdown({ selectedCollections, setSelectedCollections })
const allCollections = PersistStore(state => state.allCollections)

const deselectedOptions = useMemo(
() => allCollections.map(collection => ({
value: collection.name,
label: collection.name
() => Object.keys(allCollections).map(collection => ({
value: allCollections[collection].displayName,
label: allCollections[collection].displayName
})),
[],
);
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@ function Dashboard() {
const setHostNameMap = PersistStore(state => state.setHostNameMap)

const allCollections = PersistStore(state => state.allCollections)
const collectionsMap = PersistStore(state => state.collectionsMap)

const subCategoryMap = PersistStore(state => state.subCategoryMap)
const [eventForUser, setEventForUser] = useState({})
@@ -34,7 +33,7 @@ function Dashboard() {
const allHostNameMap = func.mapCollectionIdToHostName(apiCollections)
setHostNameMap(allHostNameMap)
setCollectionsMap(allCollectionsMap)
setAllCollections(apiCollections)
setAllCollections(func.reduceCollectionsResponse(apiCollections))
}

const fetchMetadata = async () => {
@@ -48,7 +47,7 @@ function Dashboard() {
}

useEffect(() => {
if((allCollections && allCollections.length === 0) || (Object.keys(collectionsMap).length === 0)){
if((!allCollections || Object.keys(allCollections).length === 0)){
fetchAllCollections()
}
if (!subCategoryMap || (Object.keys(subCategoryMap).length === 0)) {
Original file line number Diff line number Diff line change
@@ -40,7 +40,6 @@ function HomeDashboard() {
const [showBannerComponent, setShowBannerComponent] = useState(false)

const allCollections = PersistStore(state => state.allCollections)
const collectionsMap = PersistStore(state => state.collectionsMap)

const fetchData = async() =>{
setLoading(true)
@@ -263,9 +262,9 @@ function HomeDashboard() {
<div style={{flex: 3}}>
<VerticalStack gap={5}>
<InitialSteps initialSteps={initialSteps}/>
<ActivityTracker collections={collectionsMap} latestActivity={recentActivities} onLoadMore={handleLoadMore} showLoadMore={checkLoadMore}/>
<CoverageCard coverageObj={coverageObj} collections={allCollections} collectionsMap={collectionsMap}/>
<Pipeline riskScoreMap={riskScoreObj} collections={allCollections} collectionsMap={collectionsMap}/>
<ActivityTracker collections={allCollections} latestActivity={recentActivities} onLoadMore={handleLoadMore} showLoadMore={checkLoadMore}/>
<CoverageCard coverageObj={coverageObj} collections={allCollections}/>
<Pipeline riskScoreMap={riskScoreObj} collections={allCollections}/>
</VerticalStack>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import React from 'react'
import transform from '../transform'
import { Box, Card, Divider, HorizontalStack, ProgressBar, Scrollable, Text, VerticalStack } from '@shopify/polaris'

function CoverageCard({coverageObj, collections, collectionsMap}) {
function CoverageCard({coverageObj, collections}) {

const sortedCollectionElements = transform.formatCoverageData(coverageObj,collections)
return (
@@ -14,8 +14,8 @@ function CoverageCard({coverageObj, collections, collectionsMap}) {
{sortedCollectionElements.map((collectionObj,index)=> (
<Box padding={2} key={collectionObj.id}>
<VerticalStack gap={2}>
<Text variant="bodyMd" breakWord truncate>
{collectionsMap[collectionObj.id]}
<Text breakWord truncate variant="bodyMd">
{collectionObj?.displayName}
</Text>
<HorizontalStack gap={2}>
<Box width='85%'>
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import { Card, DataTable, Scrollable, Text,HorizontalStack , VerticalStack,Modal,Button } from '@shopify/polaris'
import { Card, DataTable, Scrollable, Text,HorizontalStack , VerticalStack,Modal } from '@shopify/polaris'
import React , {useState} from 'react'
import transform from '../transform'
import { useNavigate, Link} from "react-router-dom"


function Pipeline({riskScoreMap, collections, collectionsMap}) {
function Pipeline({riskScoreMap, collections}) {

const [active, setActive] = useState(false);

const handleShowModal = () => {
setActive(true);
};

const navigate = useNavigate();

function CicdModal({ active, setActive }) {



const primaryAction = () => {
navigate('/dashboard/settings/integrations/ci-cd');
};
@@ -62,7 +54,7 @@ function Pipeline({riskScoreMap, collections, collectionsMap}) {



const tableRows = transform.prepareTableData(riskScoreMap,collections, collectionsMap, setActive);
const tableRows = transform.prepareTableData(riskScoreMap,collections, setActive);

return (
<Card>
Loading

0 comments on commit c7026be

Please sign in to comment.