mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: last viewed project (#7191)
This commit is contained in:
parent
61a8908694
commit
dbc14fa7e9
@ -9,14 +9,14 @@ import CustomStrategiesIcon from '@mui/icons-material/ExtensionOutlined';
|
|||||||
import TagTypesIcon from '@mui/icons-material/LabelImportantOutlined';
|
import TagTypesIcon from '@mui/icons-material/LabelImportantOutlined';
|
||||||
import EnvironmentsIcon from '@mui/icons-material/CloudOutlined';
|
import EnvironmentsIcon from '@mui/icons-material/CloudOutlined';
|
||||||
import UsersIcon from '@mui/icons-material/GroupOutlined';
|
import UsersIcon from '@mui/icons-material/GroupOutlined';
|
||||||
import ServiceAccountIcon from '@mui/icons-material/SmartToyOutlined';
|
import ServiceAccountIcon from '@mui/icons-material/Computer';
|
||||||
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
|
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
|
||||||
import RoleIcon from '@mui/icons-material/AdminPanelSettingsOutlined';
|
import RoleIcon from '@mui/icons-material/AdminPanelSettingsOutlined';
|
||||||
import ApiAccessIcon from '@mui/icons-material/KeyOutlined';
|
import ApiAccessIcon from '@mui/icons-material/KeyOutlined';
|
||||||
import SingleSignOnIcon from '@mui/icons-material/AssignmentOutlined';
|
import SingleSignOnIcon from '@mui/icons-material/AssignmentOutlined';
|
||||||
import NetworkIcon from '@mui/icons-material/HubOutlined';
|
import NetworkIcon from '@mui/icons-material/HubOutlined';
|
||||||
import MaintenanceIcon from '@mui/icons-material/BuildOutlined';
|
import MaintenanceIcon from '@mui/icons-material/BuildOutlined';
|
||||||
import BannersIcon from '@mui/icons-material/PhotoOutlined';
|
import BannersIcon from '@mui/icons-material/ViewCarousel';
|
||||||
import InstanceStatsIcon from '@mui/icons-material/QueryStatsOutlined';
|
import InstanceStatsIcon from '@mui/icons-material/QueryStatsOutlined';
|
||||||
import LicenseIcon from '@mui/icons-material/ReceiptLongOutlined';
|
import LicenseIcon from '@mui/icons-material/ReceiptLongOutlined';
|
||||||
import InstancePrivacyIcon from '@mui/icons-material/ShieldOutlined';
|
import InstancePrivacyIcon from '@mui/icons-material/ShieldOutlined';
|
||||||
|
@ -4,7 +4,9 @@ import {
|
|||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
|
styled,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Typography,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { basePath } from 'utils/formatPath';
|
import { basePath } from 'utils/formatPath';
|
||||||
@ -19,6 +21,13 @@ const listItemButtonStyle = (theme: Theme) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const CappedText = styled(Typography)({
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '160px',
|
||||||
|
});
|
||||||
|
|
||||||
export const FullListItem: FC<{
|
export const FullListItem: FC<{
|
||||||
href: string;
|
href: string;
|
||||||
text: string;
|
text: string;
|
||||||
@ -38,7 +47,9 @@ export const FullListItem: FC<{
|
|||||||
<ListItemIcon sx={(theme) => ({ minWidth: theme.spacing(4) })}>
|
<ListItemIcon sx={(theme) => ({ minWidth: theme.spacing(4) })}>
|
||||||
{children}
|
{children}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText sx={{ whiteSpace: 'nowrap' }} primary={text} />
|
<ListItemText>
|
||||||
|
<CappedText>{text}</CappedText>
|
||||||
|
</ListItemText>
|
||||||
{badge}
|
{badge}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
MiniListItem,
|
MiniListItem,
|
||||||
SignOutItem,
|
SignOutItem,
|
||||||
} from './ListItems';
|
} from './ListItems';
|
||||||
import { List, styled, Tooltip, Typography } from '@mui/material';
|
import { Box, List, styled, Tooltip, Typography } from '@mui/material';
|
||||||
import { IconRenderer, StyledProjectIcon } from './IconRenderer';
|
import { IconRenderer, StyledProjectIcon } from './IconRenderer';
|
||||||
import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
|
import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
@ -98,6 +98,27 @@ export const OtherLinksList = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const RecentProjectsList: FC<{
|
||||||
|
projectId: string;
|
||||||
|
mode: NavigationMode;
|
||||||
|
onClick: () => void;
|
||||||
|
}> = ({ projectId, mode, onClick }) => {
|
||||||
|
const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List>
|
||||||
|
<DynamicListItem
|
||||||
|
href={`/projects/${projectId}`}
|
||||||
|
text={projectId}
|
||||||
|
onClick={onClick}
|
||||||
|
selected={false}
|
||||||
|
>
|
||||||
|
<StyledProjectIcon />
|
||||||
|
</DynamicListItem>
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const PrimaryNavigationList: FC<{
|
export const PrimaryNavigationList: FC<{
|
||||||
mode: NavigationMode;
|
mode: NavigationMode;
|
||||||
onClick: (activeItem: string) => void;
|
onClick: (activeItem: string) => void;
|
||||||
@ -177,3 +198,26 @@ export const SecondaryNavigation: FC<{
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const RecentProjectsNavigation: FC<{
|
||||||
|
mode: NavigationMode;
|
||||||
|
projectId: string;
|
||||||
|
onClick: () => void;
|
||||||
|
}> = ({ mode, onClick, projectId }) => {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{mode === 'full' && (
|
||||||
|
<Typography
|
||||||
|
sx={{ fontWeight: 'bold', fontSize: 'small', mb: 1, ml: 1 }}
|
||||||
|
>
|
||||||
|
Recent project
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<RecentProjectsList
|
||||||
|
projectId={projectId}
|
||||||
|
mode={mode}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -7,10 +7,12 @@ import { useExpanded } from './useExpanded';
|
|||||||
import {
|
import {
|
||||||
OtherLinksList,
|
OtherLinksList,
|
||||||
PrimaryNavigationList,
|
PrimaryNavigationList,
|
||||||
|
RecentProjectsNavigation,
|
||||||
SecondaryNavigation,
|
SecondaryNavigation,
|
||||||
SecondaryNavigationList,
|
SecondaryNavigationList,
|
||||||
} from './NavigationList';
|
} from './NavigationList';
|
||||||
import { useInitialPathname } from './useInitialPathname';
|
import { useInitialPathname } from './useInitialPathname';
|
||||||
|
import { useLastViewedProject } from 'hooks/useLastViewedProject';
|
||||||
|
|
||||||
export const MobileNavigationSidebar: FC<{ onClick: () => void }> = ({
|
export const MobileNavigationSidebar: FC<{ onClick: () => void }> = ({
|
||||||
onClick,
|
onClick,
|
||||||
@ -37,7 +39,11 @@ export const MobileNavigationSidebar: FC<{ onClick: () => void }> = ({
|
|||||||
|
|
||||||
export const StyledBox = styled(Box)(({ theme }) => ({
|
export const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
padding: theme.spacing(2, 2, 6, 2),
|
padding: theme.spacing(2, 2, 2, 2),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(3),
|
||||||
|
zIndex: theme.zIndex.tooltip,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const NavigationSidebar = () => {
|
export const NavigationSidebar = () => {
|
||||||
@ -49,6 +55,9 @@ export const NavigationSidebar = () => {
|
|||||||
|
|
||||||
const [activeItem, setActiveItem] = useState(initialPathname);
|
const [activeItem, setActiveItem] = useState(initialPathname);
|
||||||
|
|
||||||
|
const { lastViewed } = useLastViewedProject();
|
||||||
|
const showRecentProject = mode === 'full' && lastViewed;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledBox>
|
<StyledBox>
|
||||||
<PrimaryNavigationList
|
<PrimaryNavigationList
|
||||||
@ -71,21 +80,32 @@ export const NavigationSidebar = () => {
|
|||||||
activeItem={activeItem}
|
activeItem={activeItem}
|
||||||
/>
|
/>
|
||||||
</SecondaryNavigation>
|
</SecondaryNavigation>
|
||||||
<SecondaryNavigation
|
{mode === 'full' && (
|
||||||
expanded={expanded.includes('admin')}
|
<SecondaryNavigation
|
||||||
onExpandChange={(expand) => {
|
expanded={expanded.includes('admin')}
|
||||||
changeExpanded('admin', expand);
|
onExpandChange={(expand) => {
|
||||||
}}
|
changeExpanded('admin', expand);
|
||||||
mode={mode}
|
}}
|
||||||
title='Admin'
|
|
||||||
>
|
|
||||||
<SecondaryNavigationList
|
|
||||||
routes={routes.adminRoutes}
|
|
||||||
mode={mode}
|
mode={mode}
|
||||||
onClick={setActiveItem}
|
title='Admin'
|
||||||
activeItem={activeItem}
|
>
|
||||||
|
<SecondaryNavigationList
|
||||||
|
routes={routes.adminRoutes}
|
||||||
|
mode={mode}
|
||||||
|
onClick={setActiveItem}
|
||||||
|
activeItem={activeItem}
|
||||||
|
/>
|
||||||
|
</SecondaryNavigation>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showRecentProject && (
|
||||||
|
<RecentProjectsNavigation
|
||||||
|
mode={mode}
|
||||||
|
projectId={lastViewed}
|
||||||
|
onClick={() => setActiveItem('/projects')}
|
||||||
/>
|
/>
|
||||||
</SecondaryNavigation>
|
)}
|
||||||
|
|
||||||
<ShowHide
|
<ShowHide
|
||||||
mode={mode}
|
mode={mode}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Box, IconButton, styled, Tooltip } from '@mui/material';
|
import { Box, IconButton, styled, Tooltip } from '@mui/material';
|
||||||
import type { NavigationMode } from './NavigationMode';
|
import type { NavigationMode } from './NavigationMode';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
import HideIcon from '@mui/icons-material/KeyboardDoubleArrowLeft';
|
||||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
import ExpandIcon from '@mui/icons-material/KeyboardDoubleArrowRight';
|
||||||
|
|
||||||
const ShowHideWrapper = styled(Box, {
|
const ShowHideWrapper = styled(Box, {
|
||||||
shouldForwardProp: (prop) => prop !== 'mode',
|
shouldForwardProp: (prop) => prop !== 'mode',
|
||||||
@ -10,7 +10,8 @@ const ShowHideWrapper = styled(Box, {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
margin: theme.spacing(2, 1, 0, mode === 'mini' ? 1.5 : 2),
|
padding: theme.spacing(2, 1, 0, mode === 'mini' ? 1.5 : 2),
|
||||||
|
marginTop: 'auto',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -32,10 +33,13 @@ export const ShowHide: FC<{ mode: NavigationMode; onChange: () => void }> = ({
|
|||||||
)}
|
)}
|
||||||
<IconButton>
|
<IconButton>
|
||||||
{mode === 'full' ? (
|
{mode === 'full' ? (
|
||||||
<ChevronLeftIcon />
|
<HideIcon color='primary' />
|
||||||
) : (
|
) : (
|
||||||
<Tooltip title='Expand (⌘ + B)' placement='right'>
|
<Tooltip title='Expand (⌘ + B)' placement='right'>
|
||||||
<ChevronRightIcon data-testid='expand-navigation' />
|
<ExpandIcon
|
||||||
|
data-testid='expand-navigation'
|
||||||
|
color='primary'
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -5,7 +5,7 @@ import { basePath } from 'utils/formatPath';
|
|||||||
export const useLastViewedProject = () => {
|
export const useLastViewedProject = () => {
|
||||||
const key = `${basePath}:unleash-lastViewedProject`;
|
const key = `${basePath}:unleash-lastViewedProject`;
|
||||||
|
|
||||||
const [lastViewed, setLastViewed] = useState(() => {
|
const [lastViewed, setLastViewed] = useState<string | undefined>(() => {
|
||||||
return getLocalStorageItem(key);
|
return getLocalStorageItem(key);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user