change add software modal to seperate pages in Fleet UI (#21881)

This commit is contained in:
Gabriel Hernandez
2024-09-11 10:47:35 +01:00
committed by GitHub
parent 7b3cbdf4d7
commit 2b5631f891
17 changed files with 251 additions and 64 deletions
@@ -0,0 +1 @@
- change add software modal to be seperate pages in Fleet UI
@@ -0,0 +1,108 @@
import React, { useCallback } from "react";
import { Tab, TabList, Tabs } from "react-tabs";
import { InjectedRouter } from "react-router";
import { Location } from "history";
import PATHS from "router/paths";
import { buildQueryStringFromParams } from "utilities/url";
import MainContent from "components/MainContent";
import BackLink from "components/BackLink";
import TabsWrapper from "components/TabsWrapper";
const baseClass = "software-add-page";
interface IAddSoftwareSubNavItem {
name: string;
pathname: string;
}
const addSoftwareSubNav: IAddSoftwareSubNavItem[] = [
{
name: "Fleet-maintained",
pathname: PATHS.SOFTWARE_ADD_FLEET_MAINTAINED,
},
{
name: "Package",
pathname: PATHS.SOFTWARE_ADD_PACKAGE,
},
{
name: "App store (VPP)",
pathname: PATHS.SOFTWARE_ADD_APP_STORE,
},
];
const getTabIndex = (path: string): number => {
return addSoftwareSubNav.findIndex((navItem) => {
// tab stays highlighted for paths that start with same pathname
return path.startsWith(navItem.pathname);
});
};
export interface ISoftwareAddPageQueryParams {
team_id?: string;
query?: string;
page?: string;
order_key?: string;
order_direction?: "asc" | "desc";
}
interface ISoftwareAddPageProps {
children: JSX.Element;
location: Location<ISoftwareAddPageQueryParams>;
router: InjectedRouter;
}
const SoftwareAddPage = ({
children,
location,
router,
}: ISoftwareAddPageProps) => {
const navigateToNav = useCallback(
(i: number): void => {
// Only query param to persist between tabs is team id
const teamIdParam = buildQueryStringFromParams({
team_id: location?.query.team_id,
});
const navPath = addSoftwareSubNav[i].pathname.concat(`?${teamIdParam}`);
router.replace(navPath);
},
[location, router]
);
return (
<MainContent className={baseClass}>
<>
<BackLink
text="Back to software"
path={PATHS.SOFTWARE_TITLES}
className={`${baseClass}__back-to-software`}
/>
<h1>Add Software</h1>
<TabsWrapper>
<Tabs
selectedIndex={getTabIndex(location?.pathname || "")}
onSelect={navigateToNav}
>
<TabList>
{addSoftwareSubNav.map((navItem) => {
return (
<Tab key={navItem.name} data-text={navItem.name}>
{navItem.name}
</Tab>
);
})}
</TabList>
</Tabs>
</TabsWrapper>
{React.cloneElement(children, {
router,
teamId: location.query.team_id,
})}
</>
</MainContent>
);
};
export default SoftwareAddPage;
@@ -0,0 +1,23 @@
import React from "react";
import { InjectedRouter } from "react-router";
import { Location } from "history";
import { ISoftwareAddPageQueryParams } from "../SoftwareAddPage";
const baseClass = "software-app-store";
interface ISoftwareAppStoreProps {
currentTeamId: number;
router: InjectedRouter;
location: Location<ISoftwareAddPageQueryParams>;
}
const SoftwareAppStore = ({
currentTeamId,
router,
location,
}: ISoftwareAppStoreProps) => {
return <div className={baseClass}>Software App store page</div>;
};
export default SoftwareAppStore;
@@ -0,0 +1,3 @@
.software-app-store {
}
@@ -0,0 +1 @@
export { default } from "./SoftwareAppStore";
@@ -0,0 +1,39 @@
import React from "react";
import { InjectedRouter } from "react-router";
import { Location } from "history";
import { DEFAULT_QUERY } from "utilities/constants";
import { ISoftwareAddPageQueryParams } from "../SoftwareAddPage";
const baseClass = "software-fleet-maintained";
interface ISoftwareFleetMaintainedProps {
currentTeamId: number;
router: InjectedRouter;
location: Location<ISoftwareAddPageQueryParams>;
}
// default values for query params used on this page if not provided
const DEFAULT_SORT_DIRECTION = "desc";
const DEFAULT_SORT_HEADER = "hosts_count";
const DEFAULT_PAGE_SIZE = 20;
const DEFAULT_PAGE = 0;
const SoftwareFleetMaintained = ({
currentTeamId,
router,
location,
}: ISoftwareFleetMaintainedProps) => {
const {
order_key = DEFAULT_SORT_HEADER,
order_direction = DEFAULT_SORT_DIRECTION,
query = DEFAULT_QUERY,
page,
} = location.query;
const currentPage = page ? parseInt(page, 10) : DEFAULT_PAGE;
return <div className={baseClass}>Maintained Page</div>;
};
export default SoftwareFleetMaintained;
@@ -0,0 +1,3 @@
.software-fleet-maintained {
}
@@ -0,0 +1 @@
export { default } from "./SoftwareFleetMaintained";
@@ -0,0 +1,23 @@
import React from "react";
import { InjectedRouter } from "react-router";
import { Location } from "history";
import { ISoftwareAddPageQueryParams } from "../SoftwareAddPage";
const baseClass = "software-package";
interface ISoftwarePackageProps {
currentTeamId: number;
router: InjectedRouter;
location: Location<ISoftwareAddPageQueryParams>;
}
const SoftwarePackage = ({
currentTeamId,
router,
location,
}: ISoftwarePackageProps) => {
return <div className={baseClass}>Sofware package page</div>;
};
export default SoftwarePackage;
@@ -0,0 +1,3 @@
.software-package {
}
@@ -0,0 +1 @@
export { default } from "./SoftwarePackage";
@@ -0,0 +1,10 @@
.software-add-page {
&__back-to-software {
margin-bottom: $pad-medium;
}
h1 {
margin-bottom: $pad-large;
}
}
@@ -0,0 +1 @@
export { default } from "./SoftwareAddPage";
+13 -10
View File
@@ -13,7 +13,7 @@ import {
IZendeskIntegration,
IZendeskJiraIntegrations,
} from "interfaces/integration";
import { ITeamConfig } from "interfaces/team";
import { APP_CONTEXT_ALL_TEAMS_ID, ITeamConfig } from "interfaces/team";
import { IWebhookSoftwareVulnerabilities } from "interfaces/webhook";
import configAPI from "services/entities/config";
import teamsAPI, { ILoadTeamResponse } from "services/entities/teams";
@@ -255,10 +255,6 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => {
setShowManageAutomationsModal(!showManageAutomationsModal);
}, [setShowManageAutomationsModal, showManageAutomationsModal]);
const toggleAddSoftwareModal = useCallback(() => {
setShowAddSoftwareModal(!showAddSoftwareModal);
}, [showAddSoftwareModal]);
const togglePreviewPayloadModal = useCallback(() => {
setShowPreviewPayloadModal(!showPreviewPayloadModal);
}, [setShowPreviewPayloadModal, showPreviewPayloadModal]);
@@ -294,6 +290,16 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => {
}
};
const onAddSoftware = useCallback(() => {
if (currentTeamId === APP_CONTEXT_ALL_TEAMS_ID) {
setShowAddSoftwareModal(true);
} else {
router.push(
`${PATHS.SOFTWARE_ADD_FLEET_MAINTAINED}?team_id=${currentTeamId}`
);
}
}, [currentTeamId, router]);
// NOTE: used to reset page number to 0 when modifying filters
// NOTE: Solution reused from ManageHostPage.tsx
useEffect(() => {
@@ -383,7 +389,7 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => {
</Button>
)}
{canAddSoftware && (
<Button onClick={toggleAddSoftwareModal} variant="brand">
<Button onClick={onAddSoftware} variant="brand">
<span>Add software</span>
</Button>
)}
@@ -473,10 +479,7 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => {
)}
{showAddSoftwareModal && (
<AddSoftwareModal
teamId={currentTeamId ?? 0}
router={router}
onExit={toggleAddSoftwareModal}
setAddedSoftwareToken={setAddedSoftwareToken}
onExit={() => setShowAddSoftwareModal(false)}
isFreeTier={isFreeTier}
/>
)}
@@ -1,17 +1,9 @@
import React from "react";
import { InjectedRouter } from "react-router";
import { Tab, TabList, TabPanel, Tabs } from "react-tabs";
import { APP_CONTEXT_ALL_TEAMS_ID } from "interfaces/team";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
import TabsWrapper from "components/TabsWrapper";
import PremiumFeatureMessage from "components/PremiumFeatureMessage";
import AppStoreVpp from "../AppStoreVpp";
import AddPackage from "../AddPackage";
const baseClass = "add-software-modal";
interface IAllTeamsMessageProps {
@@ -35,20 +27,11 @@ const AllTeamsMessage = ({ onExit }: IAllTeamsMessageProps) => {
};
interface IAddSoftwareModalProps {
teamId: number;
router: InjectedRouter;
onExit: () => void;
setAddedSoftwareToken: (token: string) => void;
isFreeTier?: boolean;
}
const AddSoftwareModal = ({
teamId,
router,
onExit,
setAddedSoftwareToken,
isFreeTier,
}: IAddSoftwareModalProps) => {
const AddSoftwareModal = ({ onExit, isFreeTier }: IAddSoftwareModalProps) => {
const renderModalContent = () => {
if (isFreeTier) {
return (
@@ -63,45 +46,11 @@ const AddSoftwareModal = ({
);
}
if (teamId === APP_CONTEXT_ALL_TEAMS_ID) {
return <AllTeamsMessage onExit={onExit} />;
}
return (
<TabsWrapper className={`${baseClass}__tabs`}>
<Tabs>
<TabList>
<Tab>Package</Tab>
<Tab>App Store (VPP)</Tab>
</TabList>
<TabPanel>
<AddPackage
teamId={teamId}
router={router}
onExit={onExit}
setAddedSoftwareToken={setAddedSoftwareToken}
/>
</TabPanel>
<TabPanel>
<AppStoreVpp
teamId={teamId}
router={router}
onExit={onExit}
setAddedSoftwareToken={setAddedSoftwareToken}
/>
</TabPanel>
</Tabs>
</TabsWrapper>
);
return <AllTeamsMessage onExit={onExit} />;
};
return (
<Modal
title="Add software"
onExit={onExit}
width="large"
className={baseClass}
>
<Modal title="Add software" onExit={onExit} className={baseClass}>
{renderModalContent()}
</Modal>
);
+15
View File
@@ -77,6 +77,10 @@ import SoftwareVersionDetailsPage from "pages/SoftwarePage/SoftwareVersionDetail
import TeamSettings from "pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamSettings";
import SoftwareOSDetailsPage from "pages/SoftwarePage/SoftwareOSDetailsPage";
import SoftwareVulnerabilityDetailsPage from "pages/SoftwarePage/SoftwareVulnerabilityDetailsPage";
import SoftwareAddPage from "pages/SoftwarePage/SoftwareAddPage";
import SoftwareFleetMaintained from "pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained";
import SoftwarePackage from "pages/SoftwarePage/SoftwareAddPage/SoftwarePackage";
import SoftwareAppStore from "pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore";
import PATHS from "router/paths";
@@ -271,6 +275,17 @@ const routes = (
</Route>
<Route path="software">
<IndexRedirect to="titles" />
{/* we check the add route first otherwise a route like 'software/add' will be caught
* by the 'software/:id' redirect and be redirected to 'software/versions/add */}
<Route path="add" component={SoftwareAddPage}>
<IndexRedirect to="fleet-maintained" />
<Route
path="fleet-maintained"
component={SoftwareFleetMaintained}
/>
<Route path="package" component={SoftwarePackage} />
<Route path="app-store" component={SoftwareAppStore} />
</Route>
<Route component={SoftwarePage}>
<Route path="titles" component={SoftwareTitles} />
<Route path="versions" component={SoftwareTitles} />
+3
View File
@@ -75,6 +75,9 @@ export default {
SOFTWARE_VULNERABILITY_DETAILS: (cve: string): string => {
return `${URL_PREFIX}/software/vulnerabilities/${cve}`;
},
SOFTWARE_ADD_FLEET_MAINTAINED: `${URL_PREFIX}/software/add/fleet-maintained`,
SOFTWARE_ADD_PACKAGE: `${URL_PREFIX}/software/add/package`,
SOFTWARE_ADD_APP_STORE: `${URL_PREFIX}/software/add/app-store`,
// Label pages
LABEL_NEW_DYNAMIC: `${URL_PREFIX}/labels/new/dynamic`,