Skip to content

Commit c162fd4

Browse files
committed
#955 project menu to action menu + right aligned
1 parent 8f966c0 commit c162fd4

4 files changed

Lines changed: 81 additions & 135 deletions

File tree

src/frontend/src/components/ActionsMenu.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Box } from '@mui/system';
2-
import { ReactElement, useState } from 'react';
2+
import { ElementType, ReactElement, useState } from 'react';
33
import { NERButton } from './NERButton';
44
import { ArrowDropDown } from '@mui/icons-material';
55
import { Divider, ListItemIcon, Menu, MenuItem } from '@mui/material';
@@ -12,6 +12,8 @@ export type ButtonInfo = {
1212
disabled?: boolean;
1313
icon?: ReactElement;
1414
dividerTop?: boolean;
15+
component?: ElementType<any>;
16+
to?: string;
1517
};
1618

1719
interface ActionsMenuProps {
@@ -48,6 +50,14 @@ const ActionsMenu: React.FC<ActionsMenuProps> = ({ buttons, title = 'Actions' })
4850
open={dropdownOpen}
4951
anchorEl={anchorEl}
5052
onClose={handleDropdownClose}
53+
anchorOrigin={{
54+
vertical: 'bottom',
55+
horizontal: 'right'
56+
}}
57+
transformOrigin={{
58+
vertical: 'top',
59+
horizontal: 'right'
60+
}}
5161
sx={{
5262
'& .MuiPaper-root': {
5363
minWidth: anchorEl ? anchorEl.clientWidth : undefined
@@ -59,6 +69,8 @@ const ActionsMenu: React.FC<ActionsMenuProps> = ({ buttons, title = 'Actions' })
5969
button.dividerTop && <Divider key={`${index}-divider`} />,
6070
<MenuItem
6171
key={index}
72+
{...(button.component ? { component: button.component } : {})}
73+
{...(button.to ? { to: button.to } : {})}
6274
onClick={() => {
6375
handleDropdownClose();
6476
button.onClick();

src/frontend/src/components/FavoriteProjectButton.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ const FavoriteProjectButton = ({ wbsNum, projectIsFavorited }: FavoriteProjectBu
3838
sx={{
3939
color: projectIsFavorited ? 'Gold' : 'lightgray',
4040
ml: 2,
41-
mt: '3px',
41+
mt: '10px',
42+
mr: 2,
4243
maxHeight: '37.05px',
4344
maxWidth: '37.05px'
4445
}}

src/frontend/src/pages/ProjectDetailPage/ProjectViewContainer/ProjectViewContainer.tsx

Lines changed: 63 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ import { Project, isGuest, isAdmin, isLeadership } from 'shared';
88
import { projectWbsPipe, wbsPipe } from '../../../utils/pipes';
99
import ProjectDetails from './ProjectDetails';
1010
import { routes } from '../../../utils/routes';
11-
import { NERButton } from '../../../components/NERButton';
12-
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
1311
import EditIcon from '@mui/icons-material/Edit';
14-
import ListItemIcon from '@mui/material/ListItemIcon';
1512
import SyncAltIcon from '@mui/icons-material/SyncAlt';
16-
import { Box, Menu, MenuItem } from '@mui/material';
13+
import { Box } from '@mui/material';
1714
import { useState } from 'react';
1815
import { useSetProjectTeam } from '../../../hooks/projects.hooks';
1916
import { useToast } from '../../../hooks/toasts.hooks';
@@ -35,6 +32,7 @@ import SavingsIcon from '@mui/icons-material/Savings';
3532
import { TaskList } from './TaskList/v2';
3633
import { useGetMaterialsForWbsElement } from '../../../hooks/bom.hooks';
3734
import ChangeRequestTab from '../../../components/ChangeRequestTab';
35+
import ActionsMenu from '../../../components/ActionsMenu';
3836

3937
interface ProjectViewContainerProps {
4038
project: Project;
@@ -58,9 +56,8 @@ const ProjectViewContainer: React.FC<ProjectViewContainerProps> = ({ project, en
5856
const handleClickDelete = () => {
5957
setDeleteModalShow(true);
6058
};
61-
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
59+
const [_, setAnchorEl] = useState<null | HTMLElement>(null);
6260
const [tab, setTab] = useState(0);
63-
const dropdownOpen = Boolean(anchorEl);
6461

6562
if (isError) return <ErrorPage message={error.message} />;
6663
if (materialsIsError) return <ErrorPage message={materialsError.message} />;
@@ -71,10 +68,6 @@ const ProjectViewContainer: React.FC<ProjectViewContainerProps> = ({ project, en
7168
const { teamAsHeadId } = user;
7269
const projectIsFavorited = favoriteProjects.map((favoriteProject) => favoriteProject.id).includes(project.id);
7370

74-
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
75-
setAnchorEl(event.currentTarget);
76-
};
77-
7871
const handleDropdownClose = () => {
7972
setAnchorEl(null);
8073
};
@@ -96,124 +89,73 @@ const ProjectViewContainer: React.FC<ProjectViewContainerProps> = ({ project, en
9689
}
9790
};
9891

99-
const EditButton = () => (
100-
<MenuItem onClick={handleClickEdit} disabled={isGuest(user.role)}>
101-
<ListItemIcon>
102-
<EditIcon fontSize="small" />
103-
</ListItemIcon>
104-
Edit
105-
</MenuItem>
106-
);
107-
108-
const CreateChangeRequestButton = () => (
109-
<MenuItem
110-
component={Link}
111-
to={routes.CHANGE_REQUESTS_NEW_WITH_WBS + wbsPipe(project.wbsNum)}
112-
disabled={isGuest(user.role)}
113-
onClick={handleDropdownClose}
114-
>
115-
<ListItemIcon>
116-
<SyncAltIcon fontSize="small" />
117-
</ListItemIcon>
118-
Request Change
119-
</MenuItem>
120-
);
121-
122-
const SuggestBudgetIncreaseButton = () => {
123-
const budgetIncrease = materials.reduce(addMaterialCosts, 0) - project.budget;
124-
return (
125-
<MenuItem
126-
onClick={() =>
127-
history.push(
128-
`${routes.CHANGE_REQUESTS_NEW}?wbsNum=${projectWbsPipe(project.wbsNum)}&budgetChange=${budgetIncrease}`
129-
)
130-
}
131-
disabled={!isLeadership(user.role) || budgetIncrease <= 0}
132-
>
133-
<ListItemIcon>
134-
<SavingsIcon fontSize="small" />
135-
</ListItemIcon>
136-
Suggest Budget Increase
137-
</MenuItem>
138-
);
139-
};
140-
141-
const AssignToMyTeamButton = () => {
142-
const assignToTeamText = project.teams.map((team) => team.teamId).includes(teamAsHeadId!)
143-
? 'Unassign from My Team'
144-
: 'Assign to My Team';
145-
146-
return (
147-
<MenuItem onClick={handleAssignToMyTeam}>
148-
<ListItemIcon>
149-
<GroupIcon fontSize="small" />
150-
</ListItemIcon>
151-
{assignToTeamText}
152-
</MenuItem>
153-
);
154-
};
155-
156-
const buildURLForCreateWorkPackage = () => {
157-
return `${routes.WORK_PACKAGE_NEW}?wbs=${projectWbsPipe(project.wbsNum)}&crId=null`;
158-
};
159-
const CreateWorkPackageButton = () => {
160-
return (
161-
<MenuItem onClick={() => history.push(buildURLForCreateWorkPackage())} disabled={isGuest(user.role)}>
162-
<ListItemIcon>
163-
<ContentPasteIcon fontSize="small" />
164-
</ListItemIcon>
165-
Create New Work Package
166-
</MenuItem>
167-
);
168-
};
169-
170-
const DeleteButton = () => (
171-
<MenuItem onClick={handleClickDelete} disabled={!isAdmin(user.role)}>
172-
<ListItemIcon>
173-
<DeleteIcon fontSize="small" />
174-
</ListItemIcon>
175-
Delete
176-
</MenuItem>
177-
);
92+
const budgetIncrease = materials.reduce(addMaterialCosts, 0) - project.budget;
93+
const assignToTeamText = project.teams.map((team) => team.teamId).includes(teamAsHeadId!)
94+
? 'Unassign from My Team'
95+
: 'Assign to My Team';
17896

17997
const projectActionsDropdown = (
180-
<Box ml={2}>
181-
<NERButton
182-
endIcon={<ArrowDropDownIcon style={{ fontSize: 28 }} />}
183-
variant="contained"
184-
id="project-actions-dropdown"
185-
onClick={handleClick}
186-
disabled={isGuest(user.role)}
187-
>
188-
Actions
189-
</NERButton>
190-
<Menu
191-
open={dropdownOpen}
192-
anchorEl={anchorEl}
193-
onClose={handleDropdownClose}
194-
anchorOrigin={{
195-
vertical: 'bottom',
196-
horizontal: 'right'
197-
}}
198-
transformOrigin={{
199-
vertical: 'top',
200-
horizontal: 'right'
201-
}}
202-
>
203-
<EditButton />
204-
<CreateChangeRequestButton />
205-
<SuggestBudgetIncreaseButton />
206-
{teamAsHeadId && <AssignToMyTeamButton />}
207-
<CreateWorkPackageButton />
208-
<DeleteButton />
209-
</Menu>
210-
</Box>
98+
<div style={{ marginTop: '10px' }}>
99+
<ActionsMenu
100+
buttons={[
101+
{
102+
title: 'Edit',
103+
onClick: handleClickEdit,
104+
disabled: isGuest(user.role),
105+
icon: <EditIcon fontSize="small" />
106+
},
107+
{
108+
title: 'Request Change',
109+
onClick: handleDropdownClose,
110+
disabled: isGuest(user.role),
111+
icon: <SyncAltIcon fontSize="small" />,
112+
component: Link,
113+
to: routes.CHANGE_REQUESTS_NEW_WITH_WBS + wbsPipe(project.wbsNum)
114+
},
115+
{
116+
title: 'Suggest Budget Increase',
117+
onClick: () => {
118+
history.push(
119+
`${routes.CHANGE_REQUESTS_NEW}?wbsNum=${projectWbsPipe(project.wbsNum)}&budgetChange=${budgetIncrease}`
120+
);
121+
},
122+
disabled: !isLeadership(user.role) || budgetIncrease <= 0,
123+
icon: <SavingsIcon fontSize="small" />
124+
},
125+
...(teamAsHeadId
126+
? [
127+
{
128+
title: assignToTeamText,
129+
onClick: handleAssignToMyTeam,
130+
disabled: false,
131+
icon: <GroupIcon fontSize="small" />
132+
}
133+
]
134+
: []),
135+
{
136+
title: 'Create New Work Package',
137+
onClick: () => {
138+
history.push(`${routes.WORK_PACKAGE_NEW}?wbs=${projectWbsPipe(project.wbsNum)}&crId=null`);
139+
},
140+
disabled: isGuest(user.role),
141+
icon: <ContentPasteIcon fontSize="small" />
142+
},
143+
{
144+
title: 'Delete',
145+
onClick: handleClickDelete,
146+
disabled: !isAdmin(user.role),
147+
icon: <DeleteIcon fontSize="small" />,
148+
dividerTop: true
149+
}
150+
]}
151+
/>
152+
</div>
211153
);
212154

213155
const pageTitle = `${wbsPipe(project.wbsNum)} - ${project.name}`;
214156

215157
const headerRight = (
216-
<Box display="flex" justifyContent="flex-end">
158+
<Box display="flex" justifyContent="flex-end" alignItems="Center">
217159
<FavoriteProjectButton wbsNum={project.wbsNum} projectIsFavorited={projectIsFavorited} />
218160
{projectActionsDropdown}
219161
</Box>

src/frontend/src/pages/WorkPackageDetailPage/WorkPackageViewContainer/WorkPackageViewContainer.tsx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ import ActivateWorkPackageModalContainer from '../ActivateWorkPackageModalContai
1212
import WorkPackageDetails from './WorkPackageDetails';
1313
import ChangesList from '../../../components/ChangesList';
1414
import StageGateWorkPackageModalContainer from '../StageGateWorkPackageModalContainer/StageGateWorkPackageModalContainer';
15-
import { NERButton } from '../../../components/NERButton';
16-
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
17-
import { Menu, MenuItem } from '@mui/material';
18-
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
19-
import ListItemIcon from '@mui/material/ListItemIcon';
2015
import EditIcon from '@mui/icons-material/Edit';
2116
import SyncAltIcon from '@mui/icons-material/SyncAlt';
2217
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
@@ -64,10 +59,6 @@ const WorkPackageViewContainer: React.FC<WorkPackageViewContainerProps> = ({
6459
if (!dependencies || isLoading) return <LoadingIndicator />;
6560
if (isError) return <ErrorPage message={error?.message} />;
6661

67-
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
68-
setAnchorEl(event.currentTarget);
69-
};
70-
7162
const handleDropdownClose = () => {
7263
setAnchorEl(null);
7364
};
@@ -96,14 +87,14 @@ const WorkPackageViewContainer: React.FC<WorkPackageViewContainerProps> = ({
9687
title: 'Stage Gate',
9788
onClick: handleClickStageGate,
9889
disabled: !allowStageGate,
99-
icon: <DoneOutlineIcon fontSize="small" />,
90+
icon: <DoneOutlineIcon fontSize="small" />
10091
};
10192

10293
const activateButton: ButtonInfo = {
10394
title: 'Activate',
10495
onClick: handleClickActivate,
10596
disabled: !allowActivate,
106-
icon: <KeyboardDoubleArrowUpIcon fontSize="small" />,
97+
icon: <KeyboardDoubleArrowUpIcon fontSize="small" />
10798
};
10899

109100
const projectActionsDropdown = (
@@ -124,7 +115,7 @@ const WorkPackageViewContainer: React.FC<WorkPackageViewContainerProps> = ({
124115
to: routes.CHANGE_REQUESTS_NEW_WITH_WBS + wbsPipe(workPackage.wbsNum),
125116
onClick: handleDropdownClose,
126117
disabled: !allowRequestChange,
127-
icon: <SyncAltIcon fontSize="small" />,
118+
icon: <SyncAltIcon fontSize="small" />
128119
},
129120
{
130121
title: 'Delete',

0 commit comments

Comments
 (0)