mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
2651343be4
* Refresh user and config on every route change * Fix build error * Remove stray log
212 lines
6.0 KiB
TypeScript
212 lines
6.0 KiB
TypeScript
import React, { useCallback, useContext, useEffect } from "react";
|
|
import { InjectedRouter } from "react-router/lib/Router";
|
|
|
|
import { AppContext, IAppContext } from "context/app";
|
|
|
|
import TeamsDropdown from "../TeamsDropdown/TeamsDropdown";
|
|
|
|
export interface ITeamsDropdownState extends Partial<IAppContext> {
|
|
teamId?: number;
|
|
}
|
|
|
|
interface ITeamsDropdownHeaderProps {
|
|
router: InjectedRouter;
|
|
location: {
|
|
pathname: string;
|
|
query: { team_id?: string; vulnerable?: boolean };
|
|
search: string;
|
|
};
|
|
baseClass: string;
|
|
defaultTitle: string;
|
|
buttons?: (ctx: ITeamsDropdownState) => JSX.Element | null;
|
|
onChange: (ctx: ITeamsDropdownState) => void;
|
|
description: (ctx: ITeamsDropdownState) => JSX.Element | string | null;
|
|
}
|
|
|
|
const TeamsDropdownHeader = ({
|
|
router,
|
|
location,
|
|
baseClass,
|
|
defaultTitle,
|
|
buttons,
|
|
description,
|
|
onChange,
|
|
}: ITeamsDropdownHeaderProps): JSX.Element | null => {
|
|
const teamId = parseInt(location?.query?.team_id || "", 10) || 0;
|
|
|
|
const {
|
|
availableTeams,
|
|
currentUser,
|
|
currentTeam,
|
|
isPreviewMode,
|
|
isFreeTier,
|
|
isPremiumTier,
|
|
isGlobalAdmin,
|
|
isGlobalMaintainer,
|
|
isGlobalObserver,
|
|
isOnGlobalTeam,
|
|
isAnyTeamMaintainer,
|
|
isAnyTeamMaintainerOrTeamAdmin,
|
|
isAnyTeamAdmin,
|
|
isTeamAdmin,
|
|
isOnlyObserver,
|
|
setAvailableTeams,
|
|
setCurrentTeam,
|
|
setCurrentUser,
|
|
} = useContext(AppContext);
|
|
|
|
// The dropdownState is the context and local state made available to callback functions.
|
|
// Additional state/context can be made available here if needed for new uses cases.
|
|
const dropdownState = {
|
|
// NOTE: teamId is the value independently determined by this component
|
|
// and may briefly be a step ahead of the AppContext for currentTeam
|
|
// depending on the cycle of state updating and rendering
|
|
teamId,
|
|
availableTeams,
|
|
currentUser,
|
|
isPreviewMode,
|
|
isFreeTier,
|
|
isPremiumTier,
|
|
isGlobalAdmin,
|
|
isGlobalMaintainer,
|
|
isGlobalObserver,
|
|
isOnGlobalTeam,
|
|
isAnyTeamMaintainer,
|
|
isAnyTeamMaintainerOrTeamAdmin,
|
|
isAnyTeamAdmin,
|
|
isTeamAdmin,
|
|
isOnlyObserver,
|
|
};
|
|
|
|
const findAvailableTeam = (id: number) => {
|
|
return availableTeams?.find((t) => t.id === id);
|
|
};
|
|
|
|
const buildQueryString = (queryString: string, newTeamId: number) => {
|
|
queryString = queryString.startsWith("?")
|
|
? queryString.slice(1)
|
|
: queryString;
|
|
const queryParams = queryString.split("&").filter((el) => el.includes("="));
|
|
const teamIndex = queryParams.findIndex((el) => el.includes("team_id"));
|
|
|
|
if (newTeamId) {
|
|
const teamParam = `team_id=${newTeamId}`;
|
|
if (teamIndex >= 0) {
|
|
// replace old team param
|
|
queryParams.splice(teamIndex, 1, teamParam);
|
|
} else {
|
|
// add new team param
|
|
queryParams.push(teamParam);
|
|
}
|
|
} else {
|
|
// remove old team param
|
|
teamIndex >= 0 && queryParams.splice(teamIndex, 1);
|
|
}
|
|
queryString = queryParams.length ? "?".concat(queryParams.join("&")) : "";
|
|
|
|
return queryString;
|
|
};
|
|
|
|
// TODO: Add support for pages that use teamId in route params as alternative to query string
|
|
const handleTeamSelect = useCallback(
|
|
(id: number) => {
|
|
const availableTeam = findAvailableTeam(id);
|
|
setCurrentTeam(availableTeam);
|
|
const queryString = buildQueryString(location?.search, id);
|
|
if (location?.search !== queryString) {
|
|
const path = location?.pathname?.concat(queryString) || "";
|
|
!!path && router.replace(path);
|
|
}
|
|
if (onChange) {
|
|
onChange({ ...dropdownState, teamId: availableTeam?.id });
|
|
}
|
|
},
|
|
[location, router]
|
|
);
|
|
|
|
// If team_id from URL query params is not valid, we instead use a default team
|
|
// either the current team (if any) or all teams (for global users) or
|
|
// the first available team (for non-global users)
|
|
const getValidatedTeamId = () => {
|
|
if (findAvailableTeam(teamId)) {
|
|
return teamId;
|
|
}
|
|
if (!teamId && currentTeam) {
|
|
return currentTeam.id;
|
|
}
|
|
if (!teamId && !currentTeam && !isOnGlobalTeam && availableTeams) {
|
|
return availableTeams[0]?.id;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
// If team_id or currentTeam doesn't match validated id, switch to validated id
|
|
useEffect(() => {
|
|
if (availableTeams) {
|
|
const validatedId = getValidatedTeamId();
|
|
|
|
if (validatedId !== currentTeam?.id || validatedId !== teamId) {
|
|
handleTeamSelect(validatedId);
|
|
}
|
|
}
|
|
}, [availableTeams]);
|
|
|
|
const renderButtons = () => {
|
|
return buttons ? (
|
|
<div className={`${baseClass} button-wrap`}>{buttons(dropdownState)}</div>
|
|
) : null;
|
|
};
|
|
|
|
const renderDescription = () => {
|
|
const contents =
|
|
typeof description === "function"
|
|
? description(dropdownState)
|
|
: description;
|
|
|
|
return contents ? (
|
|
<div className={`${baseClass}__description`}>{contents}</div>
|
|
) : null;
|
|
};
|
|
|
|
const renderTeamsDropdown = () => {
|
|
if (!availableTeams) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className={`${baseClass}__header-wrap`}>
|
|
<div className={`${baseClass}__header`}>
|
|
<div className={`${baseClass}__text`}>
|
|
<div className={`${baseClass}__title`}>
|
|
{isFreeTier && <h1>{defaultTitle}</h1>}
|
|
{isPremiumTier &&
|
|
(availableTeams.length > 1 || isOnGlobalTeam) && (
|
|
<TeamsDropdown
|
|
currentUserTeams={availableTeams || []}
|
|
selectedTeamId={currentTeam?.id || 0}
|
|
onChange={(newSelectedValue: number) =>
|
|
handleTeamSelect(newSelectedValue)
|
|
}
|
|
/>
|
|
)}
|
|
{isPremiumTier &&
|
|
!isOnGlobalTeam &&
|
|
availableTeams.length === 1 && (
|
|
<h1>{availableTeams[0].name}</h1>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{renderButtons()}
|
|
</div>
|
|
{renderDescription()}
|
|
</>
|
|
);
|
|
};
|
|
|
|
return renderTeamsDropdown();
|
|
};
|
|
|
|
export default TeamsDropdownHeader;
|