change add software modal to seperate pages in Fleet UI (#21881)
This commit is contained in:
@@ -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";
|
||||
+39
@@ -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,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>
|
||||
);
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user