Skip to content

Commit

Permalink
feat: command bar pages and name resolving (#7397)
Browse files Browse the repository at this point in the history
  • Loading branch information
daveleek committed Jun 14, 2024
1 parent 25947c3 commit 9b789ea
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 41 deletions.
25 changes: 24 additions & 1 deletion frontend/src/component/commandBar/CommandBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
CommandResultGroup,
type CommandResultGroupItem,
} from './RecentlyVisited/CommandResultGroup';
import { PageSuggestions } from './PageSuggestions';
import { useRoutes } from 'component/layout/MainLayout/NavigationSidebar/useRoutes';
import { useAsyncDebounce } from 'react-table';
import useProjects from 'hooks/api/getters/useProjects/useProjects';

Expand Down Expand Up @@ -89,6 +91,23 @@ export const CommandBar = () => {
CommandResultGroupItem[]
>([]);
const { lastVisited } = useRecentlyVisited();
const { routes } = useRoutes();
const allRoutes: Record<
string,
{ path: string; route: string; title: string }
> = {};
for (const route of [
...routes.mainNavRoutes,
...routes.adminRoutes,
...routes.mobileRoutes,
]) {
allRoutes[route.path] = {
path: route.path,
route: route.route,
title: route.title,
};
}

const hideSuggestions = () => {
setShowSuggestions(false);
};
Expand Down Expand Up @@ -212,7 +231,11 @@ export const CommandBar = () => {
elseShow={
showSuggestions && (
<CommandResultsPaper className='dropdown-outline'>
<RecentlyVisited lastVisited={lastVisited} />
<RecentlyVisited
lastVisited={lastVisited}
routes={allRoutes}
/>
<PageSuggestions routes={allRoutes} />
</CommandResultsPaper>
)
}
Expand Down
89 changes: 89 additions & 0 deletions frontend/src/component/commandBar/PageSuggestions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {
List,
ListItemButton,
ListItemIcon,
ListItemText,
styled,
Typography,
} from '@mui/material';
import { Link } from 'react-router-dom';
import { IconRenderer } from 'component/layout/MainLayout/NavigationSidebar/IconRenderer';
import type { Theme } from '@mui/material/styles/createTheme';

const listItemButtonStyle = (theme: Theme) => ({
borderRadius: theme.spacing(0.5),
borderLeft: `${theme.spacing(0.5)} solid transparent`,
'&.Mui-selected': {
borderLeft: `${theme.spacing(0.5)} solid ${theme.palette.primary.main}`,
},
});

const StyledTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.bodySize,
padding: theme.spacing(0, 3),
}));

const StyledListItemIcon = styled(ListItemIcon)(({ theme }) => ({
minWidth: theme.spacing(4),
margin: theme.spacing(0.25, 0),
}));

const StyledListItemText = styled(ListItemText)(({ theme }) => ({
margin: 0,
}));

const toListItemData = (
items: string[],
routes: Record<string, { path: string; route: string; title: string }>,
) => {
return items.map((item) => {
return {
name: routes[item]?.title ?? item,
path: item,
icon: <IconRenderer path={item} />,
};
});
};

const pages = [
'/search',
'/integrations',
'/environments',
'/context',
'/segments',
'/tag-types',
'/applications',
'/strategies',
];

export const PageSuggestions = ({
routes,
}: {
routes: Record<string, { path: string; route: string; title: string }>;
}) => {
const filtered = pages.filter((page) => routes[page]);
const pageItems = toListItemData(filtered, routes);
return (
<>
<StyledTypography color='textSecondary'>Pages</StyledTypography>
<List>
{pageItems.map((item, index) => (
<ListItemButton
key={`recently-visited-${index}`}
dense={true}
component={Link}
to={item.path}
sx={listItemButtonStyle}
>
<StyledListItemIcon>{item.icon}</StyledListItemIcon>
<StyledListItemText>
<Typography color='textPrimary'>
{item.name}
</Typography>
</StyledListItemText>
</ListItemButton>
))}
</List>
</>
);
};
154 changes: 114 additions & 40 deletions frontend/src/component/commandBar/RecentlyVisited/RecentlyVisited.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
} from 'component/layout/MainLayout/NavigationSidebar/IconRenderer';
import type { LastViewedPage } from 'hooks/useRecentlyVisited';
import type { Theme } from '@mui/material/styles/createTheme';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';

const listItemButtonStyle = (theme: Theme) => ({
borderRadius: theme.spacing(0.5),
Expand All @@ -37,55 +39,127 @@ const StyledListItemText = styled(ListItemText)(({ theme }) => ({
margin: 0,
}));

const toListItemData = (lastVisited: LastViewedPage[]) => {
return lastVisited.map((item) => {
if (item.featureId) {
return {
name: item.featureId,
path: `/projects/${item.projectId}/features/${item.featureId}`,
icon: <Icon>{'flag'}</Icon>,
};
}
if (item.projectId) {
return {
name: item.projectId,
path: `/projects/${item.projectId}`,
icon: <StyledProjectIcon />,
};
}
return {
name: item.featureId ?? item.projectId ?? item.pathName,
path: item.pathName || '/',
icon: <IconRenderer path={item.pathName ?? '/unknown'} />,
};
});
const toListItemButton = (
item: LastViewedPage,
routes: Record<string, { path: string; route: string; title: string }>,
index: number,
) => {
const key = `recently-visited-${index}`;
if (item.featureId && item.projectId) {
return (
<RecentlyVisitedFeatureButton
key={key}
featureId={item.featureId}
projectId={item.projectId}
/>
);
}
if (item.projectId) {
return (
<RecentlyVisitedProjectButton
key={key}
projectId={item.projectId}
/>
);
}
if (!item.pathName) return null;
const name = routes[item.pathName]?.title ?? item.pathName;
return (
<RecentlyVisitedPathButton key={key} path={item.pathName} name={name} />
);
};

const RecentlyVisitedFeatureButton = ({
key,
projectId,
featureId,
}: { key: string; projectId: string; featureId: string }) => {
return (
<ListItemButton
key={key}
dense={true}
component={Link}
to={`/projects/${projectId}/features/${featureId}`}
sx={listItemButtonStyle}
>
<StyledListItemIcon>
<Icon>{'flag'}</Icon>
</StyledListItemIcon>
<StyledListItemText>
<Typography color='textPrimary'>{featureId}</Typography>
</StyledListItemText>
</ListItemButton>
);
};

const RecentlyVisitedPathButton = ({
path,
key,
name,
}: { path: string; key: string; name: string }) => {
return (
<ListItemButton
key={key}
dense={true}
component={Link}
to={path}
sx={listItemButtonStyle}
>
<StyledListItemIcon>
<ConditionallyRender
condition={path === '/projects'}
show={<StyledProjectIcon />}
elseShow={<IconRenderer path={path} />}
/>
</StyledListItemIcon>
<StyledListItemText>
<Typography color='textPrimary'>{name}</Typography>
</StyledListItemText>
</ListItemButton>
);
};

const RecentlyVisitedProjectButton = ({
projectId,
key,
}: { projectId: string; key: string }) => {
const { project, loading } = useProjectOverview(projectId);
const projectDeleted = !project.name && !loading;
if (projectDeleted) return null;
return (
<ListItemButton
key={key}
dense={true}
component={Link}
to={`/projects/${projectId}`}
sx={listItemButtonStyle}
>
<StyledListItemIcon>
<StyledProjectIcon />
</StyledListItemIcon>
<StyledListItemText>
<Typography color='textPrimary'>{project.name}</Typography>
</StyledListItemText>
</ListItemButton>
);
};

export const RecentlyVisited = ({
lastVisited,
}: { lastVisited: LastViewedPage[] }) => {
const listItems = toListItemData(lastVisited);
routes,
}: {
lastVisited: LastViewedPage[];
routes: Record<string, { path: string; route: string; title: string }>;
}) => {
const buttons = lastVisited.map((item, index) =>
toListItemButton(item, routes, index),
);
return (
<>
<StyledTypography color='textSecondary'>
Recently visited
</StyledTypography>
<List>
{listItems.map((item, index) => (
<ListItemButton
key={`recently-visited-${index}`}
dense={true}
component={Link}
to={item.path}
sx={listItemButtonStyle}
>
<StyledListItemIcon>{item.icon}</StyledListItemIcon>
<StyledListItemText>
<Typography>{item.name}</Typography>
</StyledListItemText>
</ListItemButton>
))}
</List>
<List>{buttons}</List>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import UsersIcon from '@mui/icons-material/GroupOutlined';
import ServiceAccountIcon from '@mui/icons-material/Computer';
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
import RoleIcon from '@mui/icons-material/AdminPanelSettingsOutlined';
import SearchIcon from '@mui/icons-material/Search';
import ApiAccessIcon from '@mui/icons-material/KeyOutlined';
import SingleSignOnIcon from '@mui/icons-material/AssignmentOutlined';
import NetworkIcon from '@mui/icons-material/HubOutlined';
Expand All @@ -32,6 +33,7 @@ import { styled } from '@mui/material';

// TODO: move to routes
const icons: Record<string, typeof SvgIcon> = {
'/search': SearchIcon,
'/applications': ApplicationsIcon,
'/context': ContextFieldsIcon,
'/feature-toggle-type': FlagTypesIcon,
Expand Down

0 comments on commit 9b789ea

Please sign in to comment.