diff --git a/frontend/components/LinkWithContext/LinkWithContext.tsx b/frontend/components/LinkWithContext/LinkWithContext.tsx
index 3a6d291576..4fae121674 100644
--- a/frontend/components/LinkWithContext/LinkWithContext.tsx
+++ b/frontend/components/LinkWithContext/LinkWithContext.tsx
@@ -16,6 +16,7 @@ interface ILinkWithContextProps {
names: string[];
};
className?: string;
+ disabled?: boolean;
}
const LinkWithContext = ({
@@ -24,6 +25,7 @@ const LinkWithContext = ({
to,
withParams,
className,
+ disabled = false,
}: ILinkWithContextProps): JSX.Element => {
const classNames = classnames(baseClass, className);
@@ -32,6 +34,11 @@ const LinkWithContext = ({
const newParams = pick(currentQueryParams, withParams.names);
queryString = buildQueryStringFromParams(newParams);
}
+
+ if (disabled) {
+ return {children};
+ }
+
return (
{
const [searchQueryInput, setSearchQueryInput] = useState(defaultValue);
@@ -59,6 +61,7 @@ const SearchField = ({
clearButton={clearButton}
iconPosition="start"
iconSvg={icon}
+ disabled={disabled}
/>
>
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStoreAndroid/SoftwareAppStoreAndroid.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStoreAndroid/SoftwareAppStoreAndroid.tsx
index 6c4a020a7d..008f15e179 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStoreAndroid/SoftwareAppStoreAndroid.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareAppStore/SoftwareAppStoreAndroid/SoftwareAppStoreAndroid.tsx
@@ -106,6 +106,7 @@ const SoftwareAppStoreAndroid = ({
/>
{showPreviewEndUserExperience && (
)}
diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
index b6631a41ff..c00edd3115 100644
--- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
@@ -55,6 +55,9 @@ interface IEditSoftwareModalProps {
router: InjectedRouter;
openViewYamlModal: () => void;
isIosOrIpadosApp?: boolean;
+ name: string;
+ displayName: string;
+ source?: string;
}
const EditSoftwareModal = ({
@@ -67,6 +70,9 @@ const EditSoftwareModal = ({
router,
openViewYamlModal,
isIosOrIpadosApp = false,
+ name,
+ displayName,
+ source,
}: IEditSoftwareModalProps) => {
const { renderFlash } = useContext(NotificationContext);
const { config } = useContext(AppContext);
@@ -372,6 +378,10 @@ const EditSoftwareModal = ({
)}
{showPreviewEndUserExperienceModal && (
diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx
index 6e9025f1d3..95a544128b 100644
--- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareSummaryCard/SoftwareSummaryCard.tsx
@@ -189,6 +189,9 @@ const SoftwareSummaryCard = ({
installerType={installerType}
openViewYamlModal={onToggleViewYaml}
isIosOrIpadosApp={isIosOrIpadosApp}
+ name={softwareTitle.name}
+ displayName={softwareTitle.display_name || softwareTitle.name}
+ source={softwareTitle.source}
/>
)}
{showEditConfigurationModal && softwareInstaller && teamId && (
diff --git a/frontend/pages/SoftwarePage/components/icons/SoftwareIcon/SoftwareIcon.tsx b/frontend/pages/SoftwarePage/components/icons/SoftwareIcon/SoftwareIcon.tsx
index 79c4bdaac3..c9b1332a39 100644
--- a/frontend/pages/SoftwarePage/components/icons/SoftwareIcon/SoftwareIcon.tsx
+++ b/frontend/pages/SoftwarePage/components/icons/SoftwareIcon/SoftwareIcon.tsx
@@ -29,6 +29,7 @@ const SoftwareIcon = ({
url,
uploadedAt,
}: ISoftwareIconProps) => {
+ console.log("name", name, "source", source, "url", url);
const classNames = classnames(baseClass, `${baseClass}__${size}`);
const [imgFailed, setImgFailed] = useState(false);
diff --git a/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/CategoriesEndUserExperienceModal.tsx b/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/CategoriesEndUserExperienceModal.tsx
index 6cb4bb5227..8c41ddd719 100644
--- a/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/CategoriesEndUserExperienceModal.tsx
+++ b/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/CategoriesEndUserExperienceModal.tsx
@@ -1,35 +1,203 @@
-import React from "react";
+/** Mobile preview modal uses a screenshot
+ * Non-mobile now uses HTML/CSS instead for
+ * maintainability as the self-selvice UI changes
+ *
+ * Currently only shown from the edit UI, though wired through the Add UI
+ * Users currently can set categories only when editing a curent installer
+ */
+import React, { useContext } from "react";
+
+import { Column } from "react-table";
+import { noop } from "lodash";
+import { AppContext } from "context/app";
+import { IHeaderProps } from "interfaces/datatable_config";
+
+import TableContainer from "components/TableContainer";
+import SoftwareNameCell from "components/TableContainer/DataTable/SoftwareNameCell";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
-import CategoriesEndUserExperiencePreview from "../../../../../../assets/images/categories-end-user-experience-preview-570x259@2x.png";
+import Card from "components/Card";
+import SelfServiceHeader from "pages/hosts/details/cards/Software/SelfService/components/SelfServiceHeader";
+import SearchField from "components/forms/fields/SearchField";
+import HeaderCell from "components/TableContainer/DataTable/HeaderCell";
+import CategoriesMenu from "pages/hosts/details/cards/Software/SelfService/components/CategoriesMenu";
+import { CATEGORIES_NAV_ITEMS } from "pages/hosts/details/cards/Software/SelfService/helpers";
+
import CategoriesEndUserExperiencePreviewMobile from "../../../../../../assets/images/categories-end-user-experience-preview-mobile@2x.png";
const baseClass = "categories-end-user-experience-preview-modal";
+interface ISoftwareRow {
+ name: React.ReactNode;
+}
+
+type ITableHeaderProps = IHeaderProps;
+
+const columns: Column[] = [
+ {
+ Header: (cellProps: ITableHeaderProps) => (
+
+ ),
+ accessor: "name",
+ disableSortBy: true,
+ },
+];
+
+const getData = (
+ name: string,
+ displayName: string,
+ iconUrl: string | null,
+ source?: string
+): ISoftwareRow[] => [
+ {
+ name: (
+
+ ),
+ },
+ {
+ name: (
+
+ ),
+ },
+ {
+ name: (
+
+ ),
+ },
+ {
+ name: (
+
+ ),
+ },
+];
+
+const EmptyState = () => No software found
;
+
+interface BasicSoftwareTableProps {
+ name: string;
+ displayName: string;
+ source?: string;
+ iconUrl?: string | null;
+}
+
+const BasicSoftwareTable = ({
+ name,
+ displayName,
+ source,
+ iconUrl = null,
+}: BasicSoftwareTableProps) => {
+ return (
+
+ columnConfigs={columns}
+ data={getData(name, displayName, iconUrl, source)}
+ isLoading={false}
+ emptyComponent={EmptyState}
+ showMarkAllPages={false}
+ isAllPagesSelected={false}
+ searchable={false}
+ disablePagination
+ disableCount
+ disableTableHeader
+ />
+ );
+};
+
interface ICategoriesEndUserExperienceModal {
onCancel: () => void;
isIosOrIpadosApp?: boolean;
+ name?: string;
+ displayName?: string;
+ iconUrl?: string;
+ source?: string;
}
const CategoriesEndUserExperienceModal = ({
onCancel,
isIosOrIpadosApp = false,
+ name = "Software name",
+ displayName = "Software name",
+ iconUrl,
+ source,
}: ICategoriesEndUserExperienceModal): JSX.Element => {
+ const { config } = useContext(AppContext);
return (
<>
What end users see:
-
-

-
+
+ {isIosOrIpadosApp ? (
+
+

+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+
+ )}
diff --git a/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/_styles.scss b/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/_styles.scss
index 5780a84abf..0f3ee56a5d 100644
--- a/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/_styles.scss
+++ b/frontend/pages/SoftwarePage/components/modals/CategoriesEndUserExperienceModal/_styles.scss
@@ -14,10 +14,45 @@
.modal-cta-wrap {
margin-top: 0; // Defer to new gap styling
}
+
+ &__preview-card {
+ max-height: 300px;
+ overflow: hidden;
+
+ .card-header__header {
+ font-size: initial;
+ }
+
+ &__self-service {
+ width: 515px;
+ height: 310px;
+ @include vertical-card-layout;
+ }
+
+ .categories-menu {
+ top: 0;
+ width: 200px;
+ }
+
+ .data-table__table {
+ width: 325px;
+ }
+ }
+ &__table {
+ display: flex;
+ flex-direction: row;
+ }
+
+ &__disabled-overlay {
+ position: absolute;
+ width: 540px;
+ height: 335px;
+ z-index: 1000;
+ }
}
// Pop up preview immediately.
-.modal__background:has(.calendar-event-preview-modal) {
+.modal__background:has(.categories-end-user-experience-preview-modal) {
transition: none;
animation: none;
}
diff --git a/frontend/pages/hosts/details/cards/Software/SelfService/components/CategoriesMenu/CategoriesMenu.tsx b/frontend/pages/hosts/details/cards/Software/SelfService/components/CategoriesMenu/CategoriesMenu.tsx
index 8ee4fa5053..1f5cce77fc 100644
--- a/frontend/pages/hosts/details/cards/Software/SelfService/components/CategoriesMenu/CategoriesMenu.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SelfService/components/CategoriesMenu/CategoriesMenu.tsx
@@ -13,12 +13,15 @@ export interface ICategoriesMenu {
categories: ICategory[];
queryParams: SelfServiceQueryParams;
className?: string;
+ /** Used for reusing component on self-service preview */
+ readOnly?: boolean;
}
const CategoriesMenu = ({
categories,
queryParams,
className,
+ readOnly = false,
}: ICategoriesMenu) => {
const wrapperClasses = classNames(baseClass, className);
@@ -47,6 +50,7 @@ const CategoriesMenu = ({
category_id: cat.id !== 0 ? cat.id : undefined,
}}
to={location.pathname}
+ disabled={readOnly}
>
diff --git a/frontend/pages/hosts/details/cards/Software/SelfService/components/SelfServiceHeader/SelfServiceHeader.tsx b/frontend/pages/hosts/details/cards/Software/SelfService/components/SelfServiceHeader/SelfServiceHeader.tsx
index b0533e639a..0217d0c008 100644
--- a/frontend/pages/hosts/details/cards/Software/SelfService/components/SelfServiceHeader/SelfServiceHeader.tsx
+++ b/frontend/pages/hosts/details/cards/Software/SelfService/components/SelfServiceHeader/SelfServiceHeader.tsx
@@ -7,23 +7,32 @@ const SelfServiceHeader = ({
variant,
}: {
contactUrl: string;
- variant?: "mobile-header";
-}) => (
-
- Install organization-approved apps provided by your IT department.{" "}
- {contactUrl && (
-
- If you need help,{" "}
-
-
- )}
- >
- }
- variant={variant}
- />
-);
+ variant?: "mobile-header" | "preview";
+}) => {
+ const cardHeaderVariant = variant === "mobile-header" ? variant : undefined;
+
+ return (
+
+ Install organization-approved apps provided by your IT department.{" "}
+ {contactUrl && (
+
+ If you need help,{" "}
+
+
+ )}
+ >
+ }
+ variant={cardHeaderVariant}
+ />
+ );
+};
export default SelfServiceHeader;
diff --git a/frontend/pages/labels/NewLabelPage/NewLabelPage.tsx b/frontend/pages/labels/NewLabelPage/NewLabelPage.tsx
index 368b1c71dd..cbe59ba5c0 100644
--- a/frontend/pages/labels/NewLabelPage/NewLabelPage.tsx
+++ b/frontend/pages/labels/NewLabelPage/NewLabelPage.tsx
@@ -16,8 +16,7 @@ import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
// therefore not longer needed anywhere else
import { generateTableHeaders } from "pages/labels/components/ManualLabelForm/LabelHostTargetTableConfig";
-// @ts-ignore
-import validateQuery from "components/forms/validators/validate_query";
+import { validateQuery } from "components/forms/validators/validate_query";
import { QueryContext } from "context/query";
import { AppContext } from "context/app";
diff --git a/frontend/styles/var/mixins.scss b/frontend/styles/var/mixins.scss
index 084854c5f4..7faee828f3 100644
--- a/frontend/styles/var/mixins.scss
+++ b/frontend/styles/var/mixins.scss
@@ -433,7 +433,9 @@ $max-width: 2560px;
height: 32px;
border-radius: 4px;
- a {
+ a,
+ span.link-with-context // Disabled side nav item
+ {
display: flex;
align-items: center;
height: 100%;
@@ -442,8 +444,10 @@ $max-width: 2560px;
font-weight: $regular;
text-decoration: none;
border-bottom: none;
- cursor: pointer;
+ }
+ a {
+ cursor: pointer;
&:hover {
color: $ui-fleet-black-75-over;
}