Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1606 archivable teams adjust frontend #2672

Merged
merged 18 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/backend/src/controllers/teams.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ export default class TeamsController {
static async getAllTeams(req: Request, res: Response, next: NextFunction) {
try {
const organizationId = getOrganizationId(req.headers);
const teams = await TeamsService.getAllTeams(organizationId);
const teams = await TeamsService.getAllTeams(organizationId, false);
res.status(200).json(teams);
} catch (error: unknown) {
next(error);
}
}

static async getAllArchivedTeams(req: Request, res: Response, next: NextFunction) {
try {
const organizationId = getOrganizationId(req.headers);
const teams = await TeamsService.getAllTeams(organizationId, true);
res.status(200).json(teams);
} catch (error: unknown) {
next(error);
Expand Down
4 changes: 2 additions & 2 deletions src/backend/src/routes/teams.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { body } from 'express-validator';
import { nonEmptyString, validateInputs } from '../utils/validation.utils';

const teamsRouter = express.Router();

teamsRouter.get('/', TeamsController.getAllTeams);
teamsRouter.get('/archive', TeamsController.getAllArchivedTeams);
teamsRouter.get('/:teamId', TeamsController.getSingleTeam);
teamsRouter.post(
'/:teamId/set-members',
Expand Down Expand Up @@ -39,7 +39,7 @@ teamsRouter.post(
validateInputs,
TeamsController.createTeam
);
teamsRouter.post('/:teamId/archive');
teamsRouter.post('/:teamId/archive', TeamsController.archiveTeam);

/**************** Team Type Section ****************/

Expand Down
7 changes: 5 additions & 2 deletions src/backend/src/services/teams.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ export default class TeamsService {
* @param organizationId The organization the user is currently in
* @returns a list of teams
*/
static async getAllTeams(organizationId: string): Promise<Team[]> {
static async getAllTeams(organizationId: string, onlyArchive: boolean): Promise<Team[]> {
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
const teams = await prisma.team.findMany({
where: { dateArchived: null, organizationId },
where: {
organizationId,
...(onlyArchive ? { dateArchived: { not: null } } : { dateArchived: null })
},
...getTeamQueryArgs(organizationId)
});
return teams.map(teamTransformer);
Expand Down
9 changes: 7 additions & 2 deletions src/frontend/src/apis/teams.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { apiUrls } from '../utils/urls';
import { CreateTeamPayload } from '../hooks/teams.hooks';
import { teamTransformer } from './transformers/teams.transformers';

export const getAllTeams = () => {
return axios.get<Team[]>(apiUrls.teams(), {
export const getAllTeams = (onlyArchive: boolean) => {
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
return axios.get<Team[]>(apiUrls.teams() + (onlyArchive ? '/archive' : ''), {
transformResponse: (data) => JSON.parse(data).map(teamTransformer)
});
};
Expand Down Expand Up @@ -39,6 +39,11 @@ export const setTeamHead = (id: string, userId: string) => {
});
};

export const archiveTeam = (id: string) => {
console.log(apiUrls.teamsArchive(id));
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
return axios.post<Team>(apiUrls.teamsArchive(id));
};

export const deleteTeam = (id: string) => {
return axios.post<{ message: string }>(apiUrls.teamsDelete(id));
};
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/TeamsDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface TeamDropdownProps {
}

const TeamDropdown = ({ control, name, multiselect = false }: TeamDropdownProps) => {
const { isLoading, data: teams } = useAllTeams();
const { isLoading, data: teams } = useAllTeams(false);
if (isLoading || !teams) return <LoadingIndicator />;

return (
Expand Down
25 changes: 21 additions & 4 deletions src/frontend/src/hooks/teams.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
setTeamHead,
deleteTeam,
createTeam,
setTeamLeads
setTeamLeads,
archiveTeam
} from '../apis/teams.api';

export interface CreateTeamPayload {
Expand All @@ -24,9 +25,9 @@ export interface CreateTeamPayload {
isFinanceTeam: boolean;
}

export const useAllTeams = () => {
return useQuery<Team[], Error>(['teams'], async () => {
const { data } = await getAllTeams();
export const useAllTeams = (onlyArchive: boolean) => {
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
return useQuery<Team[], Error>(['teams', onlyArchive], async () => {
const { data } = await getAllTeams(onlyArchive);
return data;
});
};
Expand Down Expand Up @@ -54,6 +55,22 @@ export const useSetTeamMembers = (teamId: string) => {
);
};

export const useArchiveTeam = (teamId: string) => {
const queryClient = useQueryClient();
return useMutation<Team, Error, string>(
['teams', 'edit'],
async () => {
const { data } = await archiveTeam(teamId);
return data;
},
{
onSuccess: () => {
queryClient.invalidateQueries(['teams']);
}
}
);
};

export const useSetTeamHead = (teamId: string) => {
const queryClient = useQueryClient();
return useMutation<Team, Error, string>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { DesignReviewStatus } from 'shared';
const AdminToolsAttendeeDesignReviewInfo: React.FC = () => {
const [searchQuery, setSearchQuery] = useState('');

const { data: allTeams, isLoading: teamsIsLoading, isError: teamsIsError, error: teamsError } = useAllTeams();
const { data: allTeams, isLoading: teamsIsLoading, isError: teamsIsError, error: teamsError } = useAllTeams(false);
const { data: allUsers, isLoading: usersIsLoading, isError: usersIsError, error: usersError } = useAllUsers();
const {
data: allDesignReviews,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import CreateTeamForm from './CreateTeamForm';
import TeamTypeTable from './TeamTypeTable';

const TeamsTools = () => {
const { data: allTeams, isLoading: allTeamsIsLoading, isError: allTeamsIsError, error: allTeamsError } = useAllTeams();
const {
data: allTeams,
isLoading: allTeamsIsLoading,
isError: allTeamsIsError,
error: allTeamsError
} = useAllTeams(false);

if (!allTeams || allTeamsIsLoading) return <LoadingIndicator />;

Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/pages/GanttPage/GanttChartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const GanttChartPage: FC = () => {
} = useAllTeamTypes();
const { isLoading: carsIsLoading, isError: carsIsError, data: cars, error: carsError } = useGetAllCars();

const { isLoading: teamsIsLoading, isError: teamsIsError, data: teams, error: teamsError } = useAllTeams();
const { isLoading: teamsIsLoading, isError: teamsIsError, data: teams, error: teamsError } = useAllTeams(false);
const [searchText, setSearchText] = useState<string>('');
const [showWorkPackagesMap, setShowWorkPackagesMap] = useState<Map<string, boolean>>(new Map());
const [addedProjects, setAddedProjects] = useState<Project[]>([]);
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/pages/SettingsPage/SettingsDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const SettingsDetails: React.FC = () => {
error: secureSettingsError,
data: userSecureSettings
} = useCurrentUserSecureSettings();
const { isLoading: allTeamsIsLoading, isError: allTeamsIsError, data: teams, error: allTeamsError } = useAllTeams();
const { isLoading: allTeamsIsLoading, isError: allTeamsIsError, data: teams, error: allTeamsError } = useAllTeams(false);

if (secureSettingsIsError) return <ErrorPage error={secureSettingsError} message={secureSettingsError.message} />;
if (settingsIsError) return <ErrorPage error={settingsError} message={settingsError.message} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const SettingsPreferences: React.FC = () => {
error: secureSettingsError,
data: userSecureSettings
} = useCurrentUserSecureSettings();
const { isLoading: allTeamsIsLoading, isError: allTeamsIsError, data: teams, error: allTeamsError } = useAllTeams();
const { isLoading: allTeamsIsLoading, isError: allTeamsIsError, data: teams, error: allTeamsError } = useAllTeams(false);

if (secureSettingsIsError) return <ErrorPage error={secureSettingsError} message={secureSettingsError.message} />;
if (settingsIsError) return <ErrorPage error={settingsError} message={settingsError.message} />;
Expand Down
20 changes: 20 additions & 0 deletions src/frontend/src/pages/TeamsPage/TeamPill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Chip } from '@mui/material';
import { red } from '@mui/material/colors';

export const TeamPill: React.FC<{
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
displayText: string;
}> = ({ displayText }) => {
return (
<Chip
size="small"
label={displayText}
variant="filled"
sx={{
fontSize: 12,
color: 'white',
backgroundColor: red[600],
width: 100
}}
/>
);
};
88 changes: 76 additions & 12 deletions src/frontend/src/pages/TeamsPage/TeamSpecificPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Grid } from '@mui/material';
import { useSingleTeam } from '../../hooks/teams.hooks';
import { Box, Grid, Menu, MenuItem, Stack } from '@mui/material';
import { useArchiveTeam, useSingleTeam } from '../../hooks/teams.hooks';
import { useParams } from 'react-router-dom';
import TeamMembersPageBlock from './TeamMembersPageBlock';
import LoadingIndicator from '../../components/LoadingIndicator';
Expand All @@ -13,53 +13,117 @@ import { Delete } from '@mui/icons-material';
import { NERButton } from '../../components/NERButton';
import { useCurrentUser } from '../../hooks/users.hooks';
import { isAdmin } from 'shared';
import { useState } from 'react';
import React, { useState } from 'react';
import DeleteTeamModal from './DeleteTeamModal';
import SetTeamTypeModal from './SetTeamTypeModal';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { TeamPill } from './TeamPill';
import { useToast } from '../../hooks/toasts.hooks';

interface ParamTypes {
teamId: string;
}

const TeamSpecificPage: React.FC = () => {
const toast = useToast();
const { teamId } = useParams<ParamTypes>();
const { isLoading, isError, data, error } = useSingleTeam(teamId);
const user = useCurrentUser();
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showTeamTypeModal, setShowTeamTypeModal] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const dropdownOpen = Boolean(anchorEl);
const { mutateAsync: archiveTeam } = useArchiveTeam(teamId);

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const handleDropdownClose = () => {
setAnchorEl(null);
};

if (isError) return <ErrorPage message={error?.message} />;
if (isLoading || !data) return <LoadingIndicator />;

const DeleteButton = () => (
<NERButton
variant="contained"
disabled={!isAdmin(user.role)}
startIcon={<Delete />}
onClick={() => setShowDeleteModal(true)}
sx={{ marginRight: '10px' }}
disabled={!isAdmin(user.role)}
>
Delete
</NERButton>
);

const SetTeamTypeButton = () => (
<NERButton variant="contained" disabled={!isAdmin(user.role)} onClick={() => setShowTeamTypeModal(true)}>
<NERButton variant="contained" onClick={() => setShowTeamTypeModal(true)} disabled={!isAdmin(user.role)}>
Set Team Type
</NERButton>
);

interface ArchiveTeamButtonProps {
archive: boolean;
}

const handleArchive = async () => {
try {
await archiveTeam(teamId);
} catch (e: unknown) {
if (e instanceof Error) {
toast.error(e.message, 3000);
}
}
};

const ArchiveTeamButton: React.FC<ArchiveTeamButtonProps> = ({ archive }) => (
<NERButton variant="contained" onClick={() => handleArchive()} disabled={!isAdmin(user.role)}>
{archive ? 'Unarchive Team' : 'Archive Team'}
</NERButton>
);

const TeamActionsDropdown = () => (
<Box>
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
<NERButton
endIcon={<ArrowDropDownIcon style={{ fontSize: 28 }} />}
variant="contained"
id="team-actions-dropdown"
onClick={handleClick}
>
Actions
</NERButton>
<Menu
open={dropdownOpen}
anchorEl={anchorEl}
onClose={handleDropdownClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
>
<MenuItem onClick={handleDropdownClose}>
<ArchiveTeamButton archive={!!data.dateArchived} />
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<DeleteButton />
</MenuItem>
</Menu>
</Box>
);

return (
<PageLayout
headerRight={
isAdmin(user.role) && (
<>
<DeleteButton />
<SetTeamTypeButton />
</>
)
<Stack direction="row" spacing={2} justifyContent="flex-end">
<SetTeamTypeButton />
{TeamActionsDropdown()}
</Stack>
}
title={`Team ${data.teamName}`}
chips={
<Box display="flex" gap="20px">
<TeamPill displayText={data.dateArchived ? 'Archived' : 'Unarchived'} />
</Box>
superhvarn marked this conversation as resolved.
Show resolved Hide resolved
}
previousPages={[{ name: 'Teams', route: routes.TEAMS }]}
>
<Grid container spacing={2}>
Expand Down
45 changes: 33 additions & 12 deletions src/frontend/src/pages/TeamsPage/TeamsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,43 @@ import TeamSummary from './TeamSummary';
import PageLayout from '../../components/PageLayout';

const TeamsPage: React.FC = () => {
const { isLoading, isError, data: teams, error } = useAllTeams();
const { isLoading: teamsLoading, isError: isTeamsError, data: teams, error: teamsError } = useAllTeams(false);

if (isLoading || !teams) return <LoadingIndicator />;
const {
isLoading: archivedTeamsLoading,
isError: isArchivedTeamsError,
data: archivedTeams,
error: archivedTeamsError
} = useAllTeams(true);
console.log(archivedTeams);

if (isError) return <ErrorPage message={error?.message} />;
if (teamsLoading || !teams) return <LoadingIndicator />;
if (archivedTeamsLoading || !archivedTeams) return <LoadingIndicator />;

if (isArchivedTeamsError) return <ErrorPage message={archivedTeamsError?.message} />;
if (isTeamsError) return <ErrorPage message={teamsError?.message} />;

return (
<PageLayout title="Teams">
<Grid container spacing={2}>
{teams.map((team) => (
<Grid item key={team.teamId}>
<TeamSummary team={team} />
</Grid>
))}
</Grid>
</PageLayout>
<>
<PageLayout title="Teams">
<Grid container spacing={2}>
{teams.map((team) => (
<Grid item key={team.teamId}>
<TeamSummary team={team} />
</Grid>
))}
</Grid>
</PageLayout>
<PageLayout title="Archived Teams">
<Grid container spacing={2}>
{archivedTeams.map((archivedTeam) => (
<Grid item key={archivedTeam.teamId}>
<TeamSummary team={archivedTeam} />
</Grid>
))}
</Grid>
</PageLayout>
</>
);
};

Expand Down
Loading
Loading