Skip to content

Commit

Permalink
console: Add improved top entity logic
Browse files Browse the repository at this point in the history
  • Loading branch information
kschiffer committed Jul 4, 2024
1 parent ab98472 commit 0cc654a
Show file tree
Hide file tree
Showing 54 changed files with 902 additions and 512 deletions.
2 changes: 0 additions & 2 deletions pkg/webui/components/delete-modal-button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ const DeleteModalButton = props => {
<ModalButton
type="button"
icon={IconTrash}
naked
danger
onApprove={handleDeleteApprove}
onCancel={onCancel}
message={message}
Expand Down
16 changes: 16 additions & 0 deletions pkg/webui/components/icon/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {
IconCpu,
IconDeviceDesktopAnalytics,
IconRouter,
IconUsersGroup,
} from '@tabler/icons-react'

import { APPLICATION, GATEWAY, END_DEVICE, ORGANIZATION } from '@console/constants/entities'

export {
IconLock as IconAccess,
IconUserShield as IconAdminPanel,
Expand Down Expand Up @@ -59,3 +68,10 @@ export {
IconUserCog as IconUserManagement,
IconCircleCheck as IconValid,
} from '@tabler/icons-react'

export const entityIcons = {
[APPLICATION]: IconDeviceDesktopAnalytics,
[GATEWAY]: IconRouter,
[END_DEVICE]: IconCpu,
[ORGANIZATION]: IconUsersGroup,
}
38 changes: 8 additions & 30 deletions pkg/webui/components/search-panel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ import { APPLICATION, END_DEVICE, GATEWAY, ORGANIZATION } from '@console/constan

import Icon, {
IconSearch,
IconApplication,
IconArrowUp,
IconArrowDown,
IconArrowBack,
IconGateway,
IconOrganization,
IconDevice,
IconX,
entityIcons,
} from '@ttn-lw/components/icon'
import ScrollFader from '@ttn-lw/components/scroll-fader'

Expand Down Expand Up @@ -73,18 +70,11 @@ const categoryMap = {
bookmarks: {
title: sharedMessages.bookmarks,
},
recency: {
'top-entities': {
title: sharedMessages.topEntities,
},
}

const iconMap = {
[APPLICATION]: IconApplication,
[GATEWAY]: IconGateway,
[ORGANIZATION]: IconOrganization,
[END_DEVICE]: IconDevice,
}

const SearchPanel = ({
onClose,
onSelect,
Expand Down Expand Up @@ -211,7 +201,7 @@ const SearchPanel = ({
</Spinner>
</div>
)}
{!topEntitiesFetching && noTopEntities && isTopEntitiesMode && (
{!topEntitiesFetching && noTopEntities && (
<div className={style.noResults}>
<LookingLuke className="d-block block-center" />
<p className="c-text-neutral-heavy mt-0 mb-0 fs-l text-center">
Expand All @@ -224,7 +214,7 @@ const SearchPanel = ({
</div>
</div>
)}
{!topEntitiesFetching && itemCount === 0 && !isTopEntitiesMode && (
{!topEntitiesFetching && itemCount === 0 && (
<div className={style.noResults}>
<LookingLuke className="d-block block-center" />
<p className="c-text-neutral-heavy mt-0 mb-0 fs-l text-center">
Expand Down Expand Up @@ -261,8 +251,8 @@ const SearchPanel = ({
item.items.forEach(subitem => {
acc.push(
<PanelItem
icon={iconMap[subitem.type]}
title={subitem.name || subitem.id}
icon={entityIcons[subitem.type]}
title={subitem?.entity?.name || subitem.id}
subtitle={subitem.id}
key={subitem.id}
isFocused={i === selectedIndex}
Expand Down Expand Up @@ -310,27 +300,15 @@ SearchPanel.propTypes = {
searchResults: PropTypes.arrayOf(
PropTypes.shape({
category: PropTypes.string.isRequired,
items: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
id: PropTypes.string.isRequired,
}),
),
items: PropTypes.arrayOf(PropTypes.unifiedEntity),
}),
).isRequired,
searchResultsFetching: PropTypes.bool.isRequired,
topEntities: PropTypes.arrayOf(
PropTypes.shape({
category: PropTypes.string.isRequired,
source: PropTypes.string.isRequired,
items: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
}),
),
items: PropTypes.arrayOf(PropTypes.unifiedEntity),
}),
).isRequired,
topEntitiesFetching: PropTypes.bool.isRequired,
Expand Down
8 changes: 6 additions & 2 deletions pkg/webui/components/search-panel/item.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ PanelItem.propTypes = {
onClick: PropTypes.func.isRequired,
onMouseEnter: PropTypes.func.isRequired,
path: PropTypes.string.isRequired,
subtitle: PropTypes.message,
title: PropTypes.message.isRequired,
subtitle: PropTypes.node,
title: PropTypes.node.isRequired,
}

PanelItem.defaultProps = {
subtitle: undefined,
}

PanelItem.defaultProps = {
Expand Down
11 changes: 1 addition & 10 deletions pkg/webui/components/sidebar/section-label/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,7 @@ import {

import { selectUser } from '@console/store/selectors/logout'

const SectionLabel = ({
label,
icon,
className,
onClick,
buttonDisabled,
'data-test-id': dataTestId,
}) => {
const SectionLabel = ({ label, icon, className, buttonDisabled, 'data-test-id': dataTestId }) => {
const user = useSelector(selectUser)
const mayViewApps = useSelector(state =>
user ? checkFromState(mayViewApplications, state) : false,
Expand Down Expand Up @@ -98,7 +91,6 @@ const SectionLabel = ({
small
icon={icon}
disabled={buttonDisabled}
onClick={onClick}
dropdownItems={plusDropdownItems}
dropdownPosition="below right"
noDropdownIcon
Expand All @@ -114,7 +106,6 @@ SectionLabel.propTypes = {
'data-test-id': PropTypes.string,
icon: PropTypes.icon.isRequired,
label: PropTypes.oneOfType([PropTypes.node, PropTypes.message]).isRequired,
onClick: PropTypes.func.isRequired,
}

SectionLabel.defaultProps = {
Expand Down
5 changes: 5 additions & 0 deletions pkg/webui/components/sidebar/side-menu/item/item.styl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
width: 100%
padding-left: $cs.xs

.title
overflow: hidden
text-overflow: ellipsis
white-space: nowrap

&.active
background: var(--c-bg-neutral-light)
color: var(--c-text-neutral-heavy)
Expand Down
1 change: 0 additions & 1 deletion pkg/webui/components/table/cell/cell.styl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

&:first-child
padding: 0 'calc(%s / 2)' % $cs.s 0 $cs.s
font-weight: $fwv2.semibold

&:last-child
padding: 0 $cs.s 0 'calc(%s / 2)' % $cs.s
Expand Down
2 changes: 2 additions & 0 deletions pkg/webui/console/constants/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const ORGANIZATION = 'ORGANIZATION'
export const USER = 'USER'
export const CLIENT = 'CLIENT'

export const ALL = 'ALL'

export const entitySdkServiceMap = Object.freeze({
application: 'Applications',
gateway: 'Gateways',
Expand Down
3 changes: 2 additions & 1 deletion pkg/webui/console/constants/event-filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export const GATEWAY_EVENTS_VERBOSE_FILTERS = [
// Regex for matching heartbeat events that trigger an update of the
// last activity display.
export const EVENT_END_DEVICE_HEARTBEAT_FILTERS_REGEXP = /^as.up\..*\.forward$/

export const EVENT_END_DEVICE_HEARTBEAT_FILTERS_STRING =
EVENT_END_DEVICE_HEARTBEAT_FILTERS_REGEXP.toString()
// Utility function to convert filter arrays to Regular Expressions strings
// that the backend accepts for applying filters.
const filterListToRegExpList = array =>
Expand Down
17 changes: 14 additions & 3 deletions pkg/webui/console/containers/search-panel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
selectSearchQuery,
selectIsSearchOpen,
} from '@console/store/selectors/search'
import { selectTopEntitiesData } from '@console/store/selectors/top-entities'

import style from './search-panel.styl'

Expand Down Expand Up @@ -72,8 +73,18 @@ const SearchPanelInner = ({ onClose }) => {
const searchQuery = useSelector(selectSearchQuery)
const [searchResultsFetching, setSearchResultsFetching] = useState(false)
const [searchResultsError, setSearchResultsError] = useState()

const [topItemsFetching, topItemsError, topEntities] = useRequest(getTopEntities())
const topEntitiesResult = useSelector(selectTopEntitiesData)
const topEntities = topEntitiesResult
? [
{
category: 'top-entities',
source: 'top-entities',
items: topEntitiesResult,
},
]
: []

const [topItemsFetching, topItemsError] = useRequest(getTopEntities())

const handleQueryChange = useCallback(
async query => {
Expand All @@ -99,7 +110,7 @@ const SearchPanelInner = ({ onClose }) => {
searchQuery={searchQuery}
searchResults={searchResults}
searchResultsFetching={searchResultsFetching}
topEntities={topEntities || []}
topEntities={topEntities}
topEntitiesFetching={topItemsFetching}
onClose={onClose}
onQueryChange={handleQueryChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
import React, { useContext } from 'react'
import { useSelector } from 'react-redux'

import { selectUserId } from '@console/store/selectors/logout'
import { selectPerEntityBookmarks } from '@console/store/selectors/user-preferences'
import { APPLICATION } from '@console/constants/entities'

import { selectApplicationTopEntities } from '@console/store/selectors/top-entities'

import SidebarContext from '../context'

import TopEntitiesSection from './top-entities-section'

const AppListSideNavigation = () => {
const topEntities = useSelector(selectPerEntityBookmarks('application'))
const topEntities = useSelector(selectApplicationTopEntities)
const { isMinimized } = useContext(SidebarContext)
const userId = useSelector(selectUserId)

if (isMinimized || topEntities.length === 0) {
// Rendering an empty div to prevent the shadow of the search bar
Expand All @@ -34,7 +34,7 @@ const AppListSideNavigation = () => {
return <div />
}

return <TopEntitiesSection topEntities={topEntities} userId={userId} entity="application" />
return <TopEntitiesSection topEntities={topEntities} type={APPLICATION} />
}

export default AppListSideNavigation
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import React, { useContext } from 'react'
import React, { useCallback, useContext } from 'react'
import { useSelector } from 'react-redux'
import { defineMessages } from 'react-intl'

import { PAGE_SIZES } from '@ttn-lw/constants/page-sizes'
import { END_DEVICE } from '@console/constants/entities'

import {
IconPuzzle,
Expand Down Expand Up @@ -60,8 +61,7 @@ import {
selectMqttProviderDisabled,
selectNatsProviderDisabled,
} from '@console/store/selectors/application-server'
import { selectPerEntityBookmarks } from '@console/store/selectors/user-preferences'
import { selectUserId } from '@console/store/selectors/logout'
import { selectEndDeviceTopEntities } from '@console/store/selectors/top-entities'

import SidebarContext from '../context'

Expand All @@ -80,8 +80,8 @@ const AppSideNavigation = () => {
const { isMinimized } = useContext(SidebarContext)
const appPageSize = getCookie('applications-list-page-size')
const appParam = `?page-size=${appPageSize ? appPageSize : PAGE_SIZES.REGULAR}`
const topEntities = useSelector(selectPerEntityBookmarks('application'))
const userId = useSelector(selectUserId)
const topEntityFilter = useCallback(e => e.id.startsWith(appId), [appId])
const topEntities = useSelector(state => selectEndDeviceTopEntities(state, topEntityFilter))

if (!app) {
return null
Expand Down Expand Up @@ -196,8 +196,8 @@ const AppSideNavigation = () => {
/>
)}
</SideNavigation>
{!isMinimized && topEntities.length > 0 && mayViewApplicationInfo.check(rights) && (
<TopEntitiesSection topEntities={topEntities} userId={userId} />
{!isMinimized && mayViewApplicationInfo.check(rights) && (
<TopEntitiesSection topEntities={topEntities} type={END_DEVICE} />
)}
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ import {
import getCookie from '@console/lib/table-utils'

import { selectUser, selectUserIsAdmin } from '@console/store/selectors/logout'
import { selectBookmarksList } from '@console/store/selectors/user-preferences'
import { selectTopEntitiesData } from '@console/store/selectors/top-entities'

import SidebarContext from '../context'

import TopEntitiesSection from './top-entities-section'

const GeneralSideNavigation = () => {
const { isMinimized } = useContext(SidebarContext)
const topEntities = useSelector(state => selectBookmarksList(state))
const topEntities = useSelector(selectTopEntitiesData)
const isUserAdmin = useSelector(selectUserIsAdmin)
const user = useSelector(selectUser)
const mayViewOrgs = useSelector(state =>
Expand Down Expand Up @@ -95,9 +95,7 @@ const GeneralSideNavigation = () => {
/>
)}
</SideNavigation>
{!isMinimized && topEntities.length > 0 && (
<TopEntitiesSection topEntities={topEntities} userId={user.ids.user_id} />
)}
{!isMinimized && <TopEntitiesSection topEntities={topEntities} />}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
import React, { useContext } from 'react'
import { useSelector } from 'react-redux'

import { GATEWAY } from '@console/constants/entities'

import SideNavigation from '@ttn-lw/components/sidebar/side-menu'

import useBookmark from '@ttn-lw/lib/hooks/use-bookmark'
import PropTypes from '@ttn-lw/lib/prop-types'

import { selectUserId } from '@console/store/selectors/logout'
import { selectPerEntityBookmarks } from '@console/store/selectors/user-preferences'
import { selectGatewayTopEntities } from '@console/store/selectors/top-entities'

import SidebarContext from '../context'

Expand All @@ -38,15 +39,14 @@ Bookmark.propTypes = {
}

const GtwListSideNavigation = () => {
const topEntities = useSelector(selectPerEntityBookmarks('gateway'))
const topEntities = useSelector(selectGatewayTopEntities)
const { isMinimized } = useContext(SidebarContext)
const userId = useSelector(selectUserId)

if (isMinimized || topEntities.length === 0) {
return <div />
}

return <TopEntitiesSection topEntities={topEntities} userId={userId} entity="gateway" />
return <TopEntitiesSection topEntities={topEntities} type={GATEWAY} />
}

export default GtwListSideNavigation
Loading

0 comments on commit 0cc654a

Please sign in to comment.