diff --git a/changes/218090-add-sofware-from-modal-to-pages b/changes/218090-add-sofware-from-modal-to-pages new file mode 100644 index 0000000000..e5c6321a7b --- /dev/null +++ b/changes/218090-add-sofware-from-modal-to-pages @@ -0,0 +1 @@ +- change add software modal to be seperate pages in Fleet UI diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx new file mode 100644 index 0000000000..8ccb94c784 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAddPage.tsx @@ -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; + 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 ( + + <> + +

Add Software

+ + + + {addSoftwareSubNav.map((navItem) => { + return ( + + {navItem.name} + + ); + })} + + + + {React.cloneElement(children, { + router, + teamId: location.query.team_id, + })} + +
+ ); +}; + +export default SoftwareAddPage; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStore.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStore.tsx new file mode 100644 index 0000000000..1d152e87ee --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStore.tsx @@ -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; +} + +const SoftwareAppStore = ({ + currentTeamId, + router, + location, +}: ISoftwareAppStoreProps) => { + return
Software App store page
; +}; + +export default SoftwareAppStore; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/_styles.scss new file mode 100644 index 0000000000..24c6a329d2 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/_styles.scss @@ -0,0 +1,3 @@ +.software-app-store { + +} diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/index.ts b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/index.ts new file mode 100644 index 0000000000..6691b50cd3 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/index.ts @@ -0,0 +1 @@ +export { default } from "./SoftwareAppStore"; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/SoftwareFleetMaintained.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/SoftwareFleetMaintained.tsx new file mode 100644 index 0000000000..aaa9627bdd --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/SoftwareFleetMaintained.tsx @@ -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; +} + +// 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
Maintained Page
; +}; + +export default SoftwareFleetMaintained; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/_styles.scss new file mode 100644 index 0000000000..a3e72e4c24 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/_styles.scss @@ -0,0 +1,3 @@ +.software-fleet-maintained { + +} diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/index.ts b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/index.ts new file mode 100644 index 0000000000..b71ab02cec --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/index.ts @@ -0,0 +1 @@ +export { default } from "./SoftwareFleetMaintained"; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/SoftwarePackage.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/SoftwarePackage.tsx new file mode 100644 index 0000000000..bb294fe622 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/SoftwarePackage.tsx @@ -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; +} + +const SoftwarePackage = ({ + currentTeamId, + router, + location, +}: ISoftwarePackageProps) => { + return
Sofware package page
; +}; + +export default SoftwarePackage; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/_styles.scss new file mode 100644 index 0000000000..e197624a52 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/_styles.scss @@ -0,0 +1,3 @@ +.software-package { + +} diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/index.ts b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/index.ts new file mode 100644 index 0000000000..c8ed0ce44b --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwarePackage/index.ts @@ -0,0 +1 @@ +export { default } from "./SoftwarePackage"; diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/_styles.scss b/frontend/pages/SoftwarePage/SoftwareAddPage/_styles.scss new file mode 100644 index 0000000000..62eae961c4 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/_styles.scss @@ -0,0 +1,10 @@ +.software-add-page { + + &__back-to-software { + margin-bottom: $pad-medium; + } + + h1 { + margin-bottom: $pad-large; + } +} diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/index.ts b/frontend/pages/SoftwarePage/SoftwareAddPage/index.ts new file mode 100644 index 0000000000..75faba6fbb --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareAddPage/index.ts @@ -0,0 +1 @@ +export { default } from "./SoftwareAddPage"; diff --git a/frontend/pages/SoftwarePage/SoftwarePage.tsx b/frontend/pages/SoftwarePage/SoftwarePage.tsx index 9dcf95250f..ed3489133d 100644 --- a/frontend/pages/SoftwarePage/SoftwarePage.tsx +++ b/frontend/pages/SoftwarePage/SoftwarePage.tsx @@ -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) => { )} {canAddSoftware && ( - )} @@ -473,10 +479,7 @@ const SoftwarePage = ({ children, router, location }: ISoftwarePageProps) => { )} {showAddSoftwareModal && ( setShowAddSoftwareModal(false)} isFreeTier={isFreeTier} /> )} diff --git a/frontend/pages/SoftwarePage/components/AddSoftwareModal/AddSoftwareModal.tsx b/frontend/pages/SoftwarePage/components/AddSoftwareModal/AddSoftwareModal.tsx index 4046e090c9..6f42d5297b 100644 --- a/frontend/pages/SoftwarePage/components/AddSoftwareModal/AddSoftwareModal.tsx +++ b/frontend/pages/SoftwarePage/components/AddSoftwareModal/AddSoftwareModal.tsx @@ -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 ; - } - - return ( - - - - Package - App Store (VPP) - - - - - - - - - - ); + return ; }; return ( - + {renderModalContent()} ); diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx index ecdb4aa9fc..7967069567 100644 --- a/frontend/router/index.tsx +++ b/frontend/router/index.tsx @@ -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 = ( + {/* 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 */} + + + + + + diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts index 3c22cd097a..ede349c3dc 100644 --- a/frontend/router/paths.ts +++ b/frontend/router/paths.ts @@ -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`,