From ef185766e713c3d722edef7185aa9b5dcc2eeae9 Mon Sep 17 00:00:00 2001 From: gillespi314 <73313222+gillespi314@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:33:05 -0600 Subject: [PATCH 1/4] Initial implementation --- .../components/FileUploader/FileUploader.tsx | 1 + frontend/components/graphics/FileCrt.tsx | 71 ++++++ frontend/components/graphics/index.ts | 2 + frontend/interfaces/pki.ts | 9 + .../cards/MdmSettings/MdmSettings.tsx | 6 + .../cards/MdmSettings/PkiPage/PkiPage.tsx | 224 ++++++++++++++++++ .../cards/MdmSettings/PkiPage/_styles.scss | 64 +++++ .../components/AddPkiModal/AddPkiModal.tsx | 164 +++++++++++++ .../components/AddPkiModal/_styles.scss | 67 ++++++ .../components/AddPkiModal/helpers.tsx | 6 + .../PkiPage/components/AddPkiModal/index.ts | 1 + .../DeletePkiModal/DeletePkiModal.tsx | 78 ++++++ .../components/DeletePkiModal/index.ts | 1 + .../EditTemplateModal/EditTemplateModal.tsx | 201 ++++++++++++++++ .../components/EditTemplateModal/index.ts | 1 + .../PkiPage/components/PkiTable/PkiTable.tsx | 58 +++++ .../components/PkiTable/PkiTableConfig.tsx | 79 ++++++ .../PkiPage/components/PkiTable/_styles.scss | 2 + .../PkiPage/components/PkiTable/index.ts | 1 + .../cards/MdmSettings/PkiPage/index.ts | 1 + .../components/PkiSection/PkiSection.tsx | 113 +++++++++ .../components/PkiSection/__styles.scss | 5 + .../components/PkiSection/index.ts | 1 + .../DownloadFileButtons/DownloadCSR.tsx | 21 +- frontend/router/index.tsx | 2 + frontend/router/paths.ts | 1 + frontend/services/entities/pki.ts | 50 ++++ frontend/utilities/endpoints.ts | 3 + 28 files changed, 1227 insertions(+), 6 deletions(-) create mode 100644 frontend/components/graphics/FileCrt.tsx create mode 100644 frontend/interfaces/pki.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/PkiPage.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/DeletePkiModal.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/index.ts create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/__styles.scss create mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/index.ts create mode 100644 frontend/services/entities/pki.ts diff --git a/frontend/components/FileUploader/FileUploader.tsx b/frontend/components/FileUploader/FileUploader.tsx index fde81e73743c..a95177197c01 100644 --- a/frontend/components/FileUploader/FileUploader.tsx +++ b/frontend/components/FileUploader/FileUploader.tsx @@ -22,6 +22,7 @@ export type ISupportedGraphicNames = Extract< | "file-p7m" | "file-pem" | "file-vpp" + | "file-crt" >; interface IFileUploaderProps { diff --git a/frontend/components/graphics/FileCrt.tsx b/frontend/components/graphics/FileCrt.tsx new file mode 100644 index 000000000000..e5c33daf9202 --- /dev/null +++ b/frontend/components/graphics/FileCrt.tsx @@ -0,0 +1,71 @@ +import React from "react"; + +const FileCrt = () => { + return ( + + + + + + + + + + + + + + + + + + + ); +}; + +export default FileCrt; diff --git a/frontend/components/graphics/index.ts b/frontend/components/graphics/index.ts index 38862d51da16..74dc39437bab 100644 --- a/frontend/components/graphics/index.ts +++ b/frontend/components/graphics/index.ts @@ -13,6 +13,7 @@ import FilePkg from "./FilePkg"; import FileP7m from "./FileP7m"; import FilePem from "./FilePem"; import FileVpp from "./FileVpp"; +import FileCrt from "./FileCrt"; import EmptyHosts from "./EmptyHosts"; import EmptyTeams from "./EmptyTeams"; import EmptyPacks from "./EmptyPacks"; @@ -48,6 +49,7 @@ export const GRAPHIC_MAP = { "file-p7m": FileP7m, "file-pem": FilePem, "file-vpp": FileVpp, + "file-crt": FileCrt, // Other graphics "collecting-results": CollectingResults, "data-error": DataError, diff --git a/frontend/interfaces/pki.ts b/frontend/interfaces/pki.ts new file mode 100644 index 000000000000..a2f1fed8cb58 --- /dev/null +++ b/frontend/interfaces/pki.ts @@ -0,0 +1,9 @@ +export type IPkiTemplate = { + profile_id: number; + name: string; + common_name: string; + san: string; + seat_id: string; +}; + +export type IPkiConfig = { name: string; templates: IPkiTemplate[] }; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx index 11774043483b..ffe48330394c 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/MdmSettings.tsx @@ -18,6 +18,7 @@ import IdpSection from "./components/IdpSection"; import EulaSection from "./components/EulaSection"; import EndUserMigrationSection from "./components/EndUserMigrationSection"; import ScepSection from "./components/ScepSection/ScepSection"; +import PkiSection from "./components/PkiSection/PkiSection"; const baseClass = "mdm-settings"; @@ -133,6 +134,11 @@ const MdmSettings = ({ router }: IMdmSettingsProps) => { isVppOn={!noVppTokenUploaded} isPremiumTier={!!isPremiumTier} /> + void; +} + +const AddPkiMessage = ({ onAddPki }: IAddPkiMessageProps) => { + return ( +
+

Add your PKI

+

Help your end users connect to Wi-Fi

+ +
+ ); +}; + +const PkiPage = ({ router }: { router: InjectedRouter }) => { + const { config, isPremiumTier } = useContext(AppContext); + + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showAddPkiModal, setShowAddPkiModal] = useState(false); + const [showEditTemplateModal, setShowEditTemplateModal] = useState(false); + + const selectedPki = useRef(null); + + const { + data: pkiConfigs, + error: errorPkiConfigs, + isLoading, + isRefetching, + refetch, + } = useQuery( + ["pkiConfigs"], + () => + Promise.resolve([ + { + name: "test_config", + templates: [ + { + profile_id: 1, + name: "test", + common_name: "test", + san: "test", + seat_id: "test", + }, + ], + }, + ]), + { + refetchOnWindowFocus: false, + retry: (tries, error) => + error.status !== 404 && error.status !== 400 && tries <= 3, + enabled: isPremiumTier, + } + ); + + const onAdd = () => { + setShowAddPkiModal(true); + }; + + const onAdded = () => { + refetch(); + setShowAddPkiModal(false); + }; + + const onEditTemplate = (pkiConfig: IPkiConfig) => { + selectedPki.current = pkiConfig; + setShowEditTemplateModal(true); + }; + + const onCancelEditTemplate = useCallback(() => { + selectedPki.current = null; + setShowEditTemplateModal(false); + }, []); + + const onEditedTemplate = useCallback(() => { + selectedPki.current = null; + refetch(); + setShowEditTemplateModal(false); + }, [refetch]); + + const onDelete = (pkiConfig: IPkiConfig) => { + selectedPki.current = pkiConfig; + setShowDeleteModal(true); + }; + + const onCancelDelete = useCallback(() => { + selectedPki.current = null; + setShowDeleteModal(false); + }, []); + + const onDeleted = useCallback(() => { + selectedPki.current = null; + refetch(); + setShowDeleteModal(false); + }, [refetch]); + + if (isLoading || isRefetching) { + return ; + } + + const showDataError = errorPkiConfigs && errorPkiConfigs.status !== 404; + + const renderContent = () => { + if (!isPremiumTier) { + return ; + } + + if (!config?.mdm.enabled_and_configured) { + return ( + + ); + } + + if (isLoading) { + return ; + } + + // TODO: error UI + if (showDataError) { + return ( +
+ +
+ ); + } + + if (!pkiConfigs?.length) { + return ; + } + + return ( + <> +

To help your end users connect to Wi-Fi, you can add your PKI.

+ + + ); + }; + + return ( + + <> + +
+
+

Public key infrastructure (PKI)

+ {isPremiumTier && + pkiConfigs?.length !== 0 && + !!config?.mdm.enabled_and_configured && ( + + )} +
+ <>{renderContent()} +
+ + {showAddPkiModal && ( + setShowAddPkiModal(false)} + /> + )} + {showDeleteModal && selectedPki.current && ( + + )} + {showEditTemplateModal && selectedPki.current && ( + + )} +
+ ); +}; + +export default PkiPage; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss new file mode 100644 index 000000000000..88edd6b4ade2 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss @@ -0,0 +1,64 @@ +.pki-page { + &__back-to-mdm { + margin-bottom: $pad-xlarge; + } + + &__page-content { + display: flex; + flex-direction: column; + gap: $pad-xxlarge; + } + + &__page-header-section { + display: flex; + flex-direction: row; + gap: $pad-large; + align-items: center; + justify-content: space-between; + + h1 { + margin-bottom: 0; + font-size: $large; + } + } + + &__add-message { + margin: 0 auto; + text-align: center; + width: 450px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + > h2 { + margin-bottom: $pad-small; + font-size: $small; + font-weight: $bold; + } + + > p { + margin: 0 0 $pad-medium; + } + } + + &__url-inputs-wrapper { + display: flex; + flex-direction: column; + gap: $pad-icon; + margin-top: $pad-large; + } + + &__url-input { + margin-bottom: 0; + } + + // TODO: remove these styles when the updated error component is used + .data-error { + margin: $pad-xxlarge 0; + padding: $pad-xlarge 0; + } + .data-error__inner { + padding: $pad-xlarge 0; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx new file mode 100644 index 000000000000..f4210750fa7c --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx @@ -0,0 +1,164 @@ +import React, { useCallback, useContext, useState } from "react"; +import { noop } from "lodash"; + +import { NotificationContext } from "context/notification"; +import pkiAPI from "services/entities/pki"; + +// @ts-ignore +import InputField from "components/forms/fields/InputField"; +import Modal from "components/Modal"; +import Button from "components/buttons/Button"; +import FileUploader from "components/FileUploader"; +import CustomLink from "components/CustomLink"; +import TooltipWrapper from "components/TooltipWrapper"; + +import DownloadCSR from "pages/admin/components/DownloadFileButtons/DownloadCSR"; + +import { getErrorMessage } from "./helpers"; + +const baseClass = "add-pki-modal"; + +interface IAddPkiModalProps { + onCancel: () => void; + onAdded: () => void; +} + +const AddPkiModal = ({ onCancel, onAdded }: IAddPkiModalProps) => { + const { renderFlash } = useContext(NotificationContext); + + const [pkiName, setPkiName] = useState(""); + const [pkiCert, setPkiCert] = useState(null); + const [isUploading, setIsUploading] = useState(false); + + const onSelectFile = useCallback((files: FileList | null) => { + const file = files?.[0]; + if (file) { + setPkiCert(file); + } + }, []); + + const uploadPkiCert = useCallback(async () => { + setIsUploading(true); + if (!pkiCert) { + setIsUploading(false); + renderFlash("error", "No file selected."); + return; + } + + try { + await pkiAPI.uploadCert(pkiName, pkiCert); + renderFlash("success", "Added successfully."); + onAdded(); + } catch (e) { + renderFlash("error", getErrorMessage(e)); + onCancel(); + } finally { + setIsUploading(false); + } + }, [pkiName, pkiCert, renderFlash, onAdded, onCancel]); + + const onInputChangeName = useCallback( + (value: string) => { + setPkiName(value); + }, + [setPkiName] + ); + + return ( + + <> +
+

To help your end users connect to Wi-Fi, you can add your PKI.

+

Fleet currently supports DigiCert PKI.

+ +
    +
  1. + 1. +

    + Download a certificate signing request (CSR) for DigiCert. + +

    +
  2. +
  3. + 2. + + + Go to{" "} + +
    +
    +
    +
  4. +
  5. + 3. + + In DigiCert, select Settings {">"} Get an RA certificate, + upload your CSR, and download your registration authority (RA) + certificate. + +
  6. +
  7. + 4. + Upload your RA certificate (.p7b file) below. +
  8. +
+
+ +
+ + + + +
+ +
+ ); +}; + +export default AddPkiModal; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss new file mode 100644 index 000000000000..ad5f957b9666 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss @@ -0,0 +1,67 @@ +.add-pki-modal { + &__request-button { + display: flex; + gap: $pad-small; + align-items: center; + margin-top: $pad-small; + + label { + display: flex; + gap: $pad-small; + cursor: pointer; + } + } + + &__setup { + display: flex; + flex-direction: column; + gap: $pad-large; + p { + margin: 0; + } + } + + &__setup-list { + font-size: $x-small; + display: flex; + flex-direction: column; + gap: $pad-large; + padding: 0; + margin: 0; + max-width: 660px; + list-style: none; + + li { + display: flex; + flex-direction: row; + gap: $pad-small; + + p { + display: flex; + flex-direction: column; + align-items: flex-start; + margin: 0; + } + } + } + + &__file-uploader { + margin-top: $pad-medium; + margin-left: $pad-medium; + + .file-uploader__message { + color: $ui-fleet-black-75; + margin: 0; + } + + button { + margin-top: 0; + } + + &--loading { + label { + opacity: 0.5; + } + } + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx new file mode 100644 index 000000000000..dcd7440790f6 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx @@ -0,0 +1,6 @@ +const DEFAULT_ERROR_MESSAGE = "Couldn't add. Please try again."; + +// eslint-disable-next-line import/prefer-default-export +export const getErrorMessage = (err: unknown) => { + return DEFAULT_ERROR_MESSAGE; +}; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/index.ts new file mode 100644 index 000000000000..598d8914fc2f --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/index.ts @@ -0,0 +1 @@ +export { default } from "./AddPkiModal"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/DeletePkiModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/DeletePkiModal.tsx new file mode 100644 index 000000000000..4838d6de7f6b --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/DeletePkiModal.tsx @@ -0,0 +1,78 @@ +import React, { useCallback, useContext, useState } from "react"; + +import { IPkiConfig } from "interfaces/pki"; +import pkiAPI from "services/entities/pki"; + +import { NotificationContext } from "context/notification"; + +import Button from "components/buttons/Button"; +import Modal from "components/Modal"; + +const baseClass = "delete-pki-modal"; + +interface IDeletePkiModalProps { + pkiConfig: IPkiConfig; + onCancel: () => void; + onDeleted: () => void; +} + +const DeletePkiModal = ({ + pkiConfig: { name }, + onCancel, + onDeleted, +}: IDeletePkiModalProps) => { + const { renderFlash } = useContext(NotificationContext); + + const [isDeleting, setIsDeleting] = useState(false); + + const onDelete = useCallback(async () => { + setIsDeleting(true); + + try { + await pkiAPI.deleteCert(name); + renderFlash("success", "Deleted successfully."); + onDeleted(); + } catch (e) { + // TODO: Check API sends back correct error messages + renderFlash("error", "Couldn’t delete. Please try again."); + onCancel(); + } + }, [onCancel, onDeleted, renderFlash, name]); + + return ( + + <> +

+ If you want to re-enable PKI, you'll have to upload a new RA + certificate. +

+ +
+ + +
+ +
+ ); +}; + +export default DeletePkiModal; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/index.ts new file mode 100644 index 000000000000..0f03a38e4bf2 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/DeletePkiModal/index.ts @@ -0,0 +1 @@ +export { default } from "./DeletePkiModal"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx new file mode 100644 index 000000000000..8b8c18ae6531 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx @@ -0,0 +1,201 @@ +import React, { useCallback, useState } from "react"; + +import { IFormField } from "interfaces/form_field"; + +import Button from "components/buttons/Button"; +// @ts-ignore +import InputField from "components/forms/fields/InputField"; +import Modal from "components/Modal"; +import TooltipWrapper from "components/TooltipWrapper"; + +import { IPkiConfig, IPkiTemplate } from "interfaces/pki"; + +const baseClass = "pki-edit-template-modal"; + +type IFormErrors = Partial>; + +const TEMPLATE_PLACEHOLDERS: Record = { + profile_id: "123", + name: "DIGICERT_TEMPLATE", + common_name: "$FLEET_VAR_HOST_HARDWARE_SERIAL@example.com", + san: "$FLEET_VAR_HOST_HARDWARE_SERIAL@example.com", + seat_id: "$FLEET_VAR_HOST_HARDWARE_SERIAL@example.com", +}; + +const TEMPLATE_HELP_TEXT: Record< + keyof IPkiTemplate, + string | React.ReactNode +> = { + profile_id: ( + + The Certificate profile ID field in DigiCert. + + ), + name: + "Letters, numbers, and underscores only. Fleet will create a configuration profile variable with the $FLEET_VAR_PKI_CERT_ prefix (e.g. $FLEET_VAR_PKI_CERT_DIGICERT_TEMPALTE).", + common_name: "Certificates delivered to your hosts using will have this CN.", + san: "Certificates delivered to your hosts using will have this SAN.", + seat_id: + "Certificates delivered to your hosts using will be assgined to this seat ID in DigiCert.", +}; + +const EditTemplateModal = ({ + pkiConfig, + onCancel, + onSuccess, +}: { + pkiConfig: IPkiConfig; + onCancel: () => void; + onSuccess: () => void; +}) => { + const [formData, setFormData] = useState( + pkiConfig.templates[0] || { + profile_id: "", + name: "", + common_name: "", + san: "", + seat_id: "", + } + ); + const [formErrors, setFormErrors] = useState({}); + + const onInputChange = ({ name, value }: IFormField) => { + setFormErrors((prev) => ({ ...prev, [name]: "" })); + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const onSubmit = useCallback(async () => { + // validate + const errors: IFormErrors = {}; + + if (!formData.profile_id) { + errors.profile_id = "Profile ID is required"; + } + + if (!formData.name) { + errors.name = "Name is required"; + } + + if (!formData.common_name) { + errors.common_name = "Common name is required"; + } + + if (!formData.san) { + errors.san = "Subject alternative name is required"; + } + + if (!formData.seat_id) { + errors.seat_id = "Seat ID is required"; + } + + if (Object.keys(errors).length) { + setFormErrors(errors); + return; + } + + // save + console.log("Save", formData); + onSuccess(); + }, [formData, onSuccess]); + + const disableInput = !pkiConfig.templates.length; + const disableSave = Object.values(formData).some((v) => !v); + + const isSaving = false; + + return ( + + <> +
+ + + + + +
+ + + + +
+ + +
+ ); +}; + +export default EditTemplateModal; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/index.ts new file mode 100644 index 000000000000..f9b2f3042d7f --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/index.ts @@ -0,0 +1 @@ +export { default } from "./EditTemplateModal"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx new file mode 100644 index 000000000000..97466306241f --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx @@ -0,0 +1,58 @@ +import React from "react"; + +import { IPkiConfig } from "interfaces/pki"; + +import TableContainer from "components/TableContainer"; + +import { generateTableConfig } from "./PkiTableConfig"; + +const baseClass = "pki-table"; + +interface IPkiTableProps { + data: IPkiConfig[]; + // onEditTokenTeam: (token: IPkiConfig) => void; + onEdit: (pkiConfig: IPkiConfig) => void; + onDelete: (pkiConfig: IPkiConfig) => void; +} + +const PkiTable = ({ + data, + // onEditTokenTeam, + onEdit, + onDelete, +}: IPkiTableProps) => { + const onSelectAction = (action: string, pkiConfig: IPkiConfig) => { + switch (action) { + case "view_template": + onEdit(pkiConfig); + break; + // case "add_template": + // onRenewToken(pkiConfig); + // break; + case "delete": + onDelete(pkiConfig); + break; + default: + break; + } + }; + + const tableConfig = generateTableConfig(onSelectAction); + + return ( + + columnConfigs={tableConfig} + defaultSortHeader="org_name" + disableTableHeader + disablePagination + showMarkAllPages={false} + isAllPagesSelected={false} + emptyComponent={() => <>} + isLoading={false} + data={data} + className={baseClass} + /> + ); +}; + +export default PkiTable; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx new file mode 100644 index 000000000000..0785c21ea40d --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { CellProps, Column } from "react-table"; + +import { IMdmAbmToken } from "interfaces/mdm"; +import { IPkiConfig } from "interfaces/pki"; +import { IHeaderProps, IStringCellProps } from "interfaces/datatable_config"; +import { IDropdownOption } from "interfaces/dropdownOption"; + +import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; +import ActionsDropdown from "components/ActionsDropdown"; +import TextCell from "components/TableContainer/DataTable/TextCell"; +import TooltipWrapper from "components/TooltipWrapper"; + +import RenewDateCell from "../../../components/RenewDateCell"; +import OrgNameCell from "./OrgNameCell"; +import { IRenewDateCellStatusConfig } from "../../../components/RenewDateCell/RenewDateCell"; + +type IPkiTableConfig = Column; +type ITableStringCellProps = IStringCellProps; +type IPkiTemplatesCellProps = CellProps; + +type ITableHeaderProps = IHeaderProps; + +const DEFAULT_ACTION_OPTIONS: IDropdownOption[] = [ + { value: "view_template", label: "View template", disabled: false }, + // { value: "renew", label: "Renew", disabled: false }, + { value: "delete", label: "Delete", disabled: false }, +]; + +const generateActions = () => { + return DEFAULT_ACTION_OPTIONS; +}; + +export const generateTableConfig = ( + actionSelectHandler: (value: string, pkiConfig: IPkiConfig) => void +): IPkiTableConfig[] => { + return [ + { + accessor: "name", + sortType: "caseInsensitive", + Header: (cellProps: ITableHeaderProps) => ( + + ), + Cell: (cellProps: ITableStringCellProps) => { + const { name } = cellProps.cell.row.original; + return ; + }, + }, + { + accessor: "templates", + Header: "Certificate template", + disableSortBy: true, + Cell: ({ value: templates }: IPkiTemplatesCellProps) => { + return ; // TODO: use our own icon + }, + }, + { + Header: "", + id: "actions", + disableSortBy: true, + // the accessor here is insignificant, we just need it as its required + // but we don't use it. + accessor: () => "name", + Cell: (cellProps: CellProps) => ( + + actionSelectHandler(action, cellProps.row.original) + } + placeholder="Actions" + /> + ), + }, + ]; +}; + +export const generateTableData = (data: IMdmAbmToken[]) => { + return data; +}; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss new file mode 100644 index 000000000000..844f82633e0e --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss @@ -0,0 +1,2 @@ +.pki-table { +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/index.ts new file mode 100644 index 000000000000..9ad8cd8cfa0b --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/index.ts @@ -0,0 +1 @@ +export { default } from "./PkiTable"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/index.ts new file mode 100644 index 000000000000..50ce02284801 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/index.ts @@ -0,0 +1 @@ +export { default } from "./PkiPage"; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx new file mode 100644 index 000000000000..43791a2cd59e --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx @@ -0,0 +1,113 @@ +import React, { useContext } from "react"; +import { InjectedRouter } from "react-router"; + +import PATHS from "router/paths"; +import { AppContext } from "context/app"; + +import Button from "components/buttons/Button"; +import Icon from "components/Icon"; + +import SettingsSection from "pages/admin/components/SettingsSection"; +import PremiumFeatureMessage from "components/PremiumFeatureMessage"; +import TooltipWrapper from "components/TooltipWrapper"; + +import SectionCard from "../SectionCard"; + +const baseClass = "pki-section"; + +interface IPkiCardProps { + isAppleMdmOn: boolean; + isPkiOn: boolean; + router: InjectedRouter; +} + +export const PKI_TIP_CONTENT = <>Fleet currently supports DigiCert as a PKI.; + +const DIGICERT_PKI_ADDED_MESSAGE = "DigiCert added as your PKI."; // TODO: confirm this message + +const PkiCard = ({ isAppleMdmOn, isPkiOn, router }: IPkiCardProps) => { + const navigateToPkiSetup = () => { + router.push(PATHS.ADMIN_INTEGRATIONS_PKI); + }; + + const appleMdmDisabledCard = ( + +

+ To help your end users connect to Wi-Fi by adding your{" "} + PKI, first + turn on Apple (macOS, iOS, iPadOS) MDM. +

+
+ ); + + const isPkiOnCard = ( + + + Edit + + } + > + {DIGICERT_PKI_ADDED_MESSAGE} + + ); + + const isPkiOffCard = ( + + Add PKI + + } + > +
+ To help your end users connect to Wi-Fi, you can add your{" "} + PKI. +
+
+ ); + + if (!isAppleMdmOn) { + return appleMdmDisabledCard; + } + + return isPkiOn ? isPkiOnCard : isPkiOffCard; +}; + +interface IPkiSectionProps { + router: InjectedRouter; + isPkiOn: boolean; + isPremiumTier: boolean; +} + +const PkiSection = ({ router, isPkiOn, isPremiumTier }: IPkiSectionProps) => { + const { config } = useContext(AppContext); + + return ( + + {!isPremiumTier ? ( + + ) : ( + + )} + + ); +}; + +export default PkiSection; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/__styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/__styles.scss new file mode 100644 index 000000000000..da5a66396361 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/__styles.scss @@ -0,0 +1,5 @@ +.pki-section { + .section-card__content-wrapper span { + display: inline-flex; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/index.ts b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/index.ts new file mode 100644 index 000000000000..95b76aac0088 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/index.ts @@ -0,0 +1 @@ +export { default } from "./PkiSection"; diff --git a/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx b/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx index a1e82da21bac..25f60e13ae6e 100644 --- a/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx +++ b/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx @@ -1,6 +1,7 @@ import React, { FormEvent, useCallback, useMemo, useState } from "react"; import mdmAppleApi from "services/entities/mdm_apple"; +import pkiApi from "services/entities/pki"; import Icon from "components/Icon"; import Button from "components/buttons/Button"; @@ -10,10 +11,11 @@ interface IDownloadCSRProps { baseClass: string; onSuccess?: () => void; onError?: (e: unknown) => void; + pkiName?: string; } -const downloadCSRFile = (data: { csr: string }) => { - downloadBase64ToFile(data.csr, "fleet-mdm-apple.csr"); +const downloadCSRFile = (data: { csr: string }, filename?: string) => { + downloadBase64ToFile(data.csr, filename || "fleet-mdm-apple.csr"); }; // TODO: why can't we use Content-Dispostion for these? We're only getting one file back now. @@ -21,6 +23,7 @@ const downloadCSRFile = (data: { csr: string }) => { const useDownloadCSR = ({ onSuccess, onError, + pkiName, }: Omit) => { const [downloadState, setDownloadState] = useState(undefined); @@ -29,8 +32,13 @@ const useDownloadCSR = ({ evt.preventDefault(); setDownloadState("loading"); try { - const data = await mdmAppleApi.requestCSR(); - downloadCSRFile(data); + let data; + if (pkiName) { + data = await pkiApi.requestCSR(pkiName); + } else { + data = await mdmAppleApi.requestCSR(); + } + downloadCSRFile(data, pkiName); setDownloadState("success"); onSuccess && onSuccess(); } catch (e) { @@ -38,7 +46,7 @@ const useDownloadCSR = ({ onError && onError(e); } }, - [onError, onSuccess] + [onError, onSuccess, pkiName] ); const memoized = useMemo( @@ -56,8 +64,9 @@ export const DownloadCSR = ({ baseClass, onSuccess, onError, + pkiName, }: IDownloadCSRProps) => { - const { handleDownload } = useDownloadCSR({ onSuccess, onError }); + const { handleDownload } = useDownloadCSR({ onSuccess, onError, pkiName }); return ( - - + ) : ( + <> + + + + + + )} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss new file mode 100644 index 000000000000..efacee0275f7 --- /dev/null +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss @@ -0,0 +1,8 @@ +.pki-edit-template-modal { + .form-field__label { + color: $core-fleet-black; + } + .input-field--disabled { + color: $core-fleet-black; + } +} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx index 493af7df82ad..056396bb6daf 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx @@ -9,6 +9,7 @@ import { IDropdownOption } from "interfaces/dropdownOption"; import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; import ActionsDropdown from "components/ActionsDropdown"; import TextCell from "components/TableContainer/DataTable/TextCell"; +import StatusIndicatorWithIcon from "components/StatusIndicatorWithIcon"; type IPkiTableConfig = Column; type ITableStringCellProps = IStringCellProps; @@ -23,11 +24,11 @@ const generateActions = (pkiConfig: IPkiConfig): IDropdownOption[] => { label: pkiConfig.templates?.length ? "View template" : "Add template", disabled: false, }, - { - value: "delete", - label: "Delete", - disabled: false, - }, + // { + // value: "delete", + // label: "Delete", + // disabled: false, + // }, ]; }; @@ -51,7 +52,11 @@ export const generateTableConfig = ( Header: "Certificate template", disableSortBy: true, Cell: ({ value: templates }: IPkiTemplatesCellProps) => { - return ; // TODO: use our own icon + return templates.length ? ( + + ) : ( + + ); }, }, { diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx index 43791a2cd59e..eb636bc6bfec 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/PkiSection/PkiSection.tsx @@ -44,10 +44,19 @@ const PkiCard = ({ isAppleMdmOn, isPkiOn, router }: IPkiCardProps) => { + // + // Edit + // + // } cta={ - } > diff --git a/frontend/services/entities/pki.ts b/frontend/services/entities/pki.ts index e4244017dbc5..536805a93f5b 100644 --- a/frontend/services/entities/pki.ts +++ b/frontend/services/entities/pki.ts @@ -35,16 +35,11 @@ const pkiServive = { return sendRequest("GET", path); }, - addTemplate: (pkiName: string, template: IPkiTemplate) => { + patchIntegrationsDigicertPki: (pkiConfigs: IPkiConfig[]) => { const { CONFIG } = endpoints; const formData = { integrations: { - digicert_pki: [ - { - pki_name: pkiName, - templates: [template], - }, - ], + digicert_pki: pkiConfigs, }, }; From c522756903ef1059928a151561c5da4cc3f102b6 Mon Sep 17 00:00:00 2001 From: gillespi314 <73313222+gillespi314@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:46:04 -0600 Subject: [PATCH 4/4] Refine UI for PKI integrations --- .../StatusIndicatorWithIcon.tsx | 2 + .../cards/MdmSettings/PkiPage/_styles.scss | 4 ++ .../components/AddPkiModal/AddPkiModal.tsx | 6 ++- .../components/AddPkiModal/_styles.scss | 5 +++ .../components/AddPkiModal/helpers.tsx | 2 +- .../EditTemplateModal/EditTemplateModal.tsx | 11 ++--- .../components/EditTemplateModal/_styles.scss | 5 +++ .../PkiPage/components/PkiTable/PkiTable.tsx | 2 +- .../components/PkiTable/PkiTableConfig.tsx | 8 +++- .../PkiPage/components/PkiTable/_styles.scss | 2 - .../DownloadFileButtons/DownloadCSR.tsx | 44 ++++++++++++++----- 11 files changed, 68 insertions(+), 23 deletions(-) delete mode 100644 frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss diff --git a/frontend/components/StatusIndicatorWithIcon/StatusIndicatorWithIcon.tsx b/frontend/components/StatusIndicatorWithIcon/StatusIndicatorWithIcon.tsx index e0ae3cd20d7c..1bcaa4529eff 100644 --- a/frontend/components/StatusIndicatorWithIcon/StatusIndicatorWithIcon.tsx +++ b/frontend/components/StatusIndicatorWithIcon/StatusIndicatorWithIcon.tsx @@ -78,6 +78,8 @@ const StatusIndicatorWithIcon = ({ {valueContent} ); + // FIXME: It seems like this needs to include the __value class to work properly (otherwise the + // icon formatting is off). return
{indicatorContent}
; }; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss index 88edd6b4ade2..58fb51d3ba83 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/_styles.scss @@ -1,4 +1,8 @@ .pki-page { + p { + margin: 0; + } + &__back-to-mdm { margin-bottom: $pad-xlarge; } diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx index f4210750fa7c..94ea7b2fb726 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/AddPkiModal.tsx @@ -47,7 +47,7 @@ const AddPkiModal = ({ onCancel, onAdded }: IAddPkiModalProps) => { try { await pkiAPI.uploadCert(pkiName, pkiCert); - renderFlash("success", "Added successfully."); + renderFlash("success", "Successfully added your PKI."); onAdded(); } catch (e) { renderFlash("error", getErrorMessage(e)); @@ -80,6 +80,7 @@ const AddPkiModal = ({ onCancel, onAdded }: IAddPkiModalProps) => { onChange={onInputChangeName} name="pkiName" value={pkiName} + placeholder="PKI name" />
  1. @@ -88,9 +89,12 @@ const AddPkiModal = ({ onCancel, onAdded }: IAddPkiModalProps) => { Download a certificate signing request (CSR) for DigiCert.

  2. diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss index ad5f957b9666..419a22338e4d 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/_styles.scss @@ -1,4 +1,9 @@ .add-pki-modal { + .input-field { + &::placeholder { + font-style: normal; // FIXME: Should this be a global style change instead? See https://github.com/fleetdm/fleet/blob/99d962036774357e6767b9b84470c1d40b007c3b/frontend/components/Modal/_styles.scss#L36 + } + } &__request-button { display: flex; gap: $pad-small; diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx index dcd7440790f6..a1495ea75d57 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/AddPkiModal/helpers.tsx @@ -1,4 +1,4 @@ -const DEFAULT_ERROR_MESSAGE = "Couldn't add. Please try again."; +const DEFAULT_ERROR_MESSAGE = "Couldn't add your PKI. Please try again."; // eslint-disable-next-line import/prefer-default-export export const getErrorMessage = (err: unknown) => { diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx index f98d90d4916a..687982a39d79 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/EditTemplateModal.tsx @@ -139,12 +139,13 @@ const EditTemplateModal = ({ [formData, onSuccess, selectedConfig.pki_name, byPkiName, renderFlash] ); - const disableInput = !!selectedConfig.templates.length; - const disableSave = Object.values(formData).some((v) => !v); + const isViewOnly = !!selectedConfig.templates.length; + const disableInput = isViewOnly || isSaving; + const disableSave = disableInput || Object.values(formData).some((v) => !v); return ( <> @@ -210,7 +211,7 @@ const EditTemplateModal = ({ disabled={disableInput} />
    - {disableInput ? ( + {isViewOnly ? ( @@ -228,7 +229,7 @@ const EditTemplateModal = ({ variant="brand" type="submit" isLoading={isSaving} - disabled={disableSave} + disabled={disableSave || isSaving} > Save diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss index efacee0275f7..d8019005fc7d 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/EditTemplateModal/_styles.scss @@ -1,4 +1,9 @@ .pki-edit-template-modal { + .input-field { + &::placeholder { + font-style: normal; // FIXME: Should this be a global style change instead? See https://github.com/fleetdm/fleet/blob/99d962036774357e6767b9b84470c1d40b007c3b/frontend/components/Modal/_styles.scss#L36 + } + } .form-field__label { color: $core-fleet-black; } diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx index 5ab07ce03c94..5a7708fec990 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTable.tsx @@ -36,7 +36,7 @@ const PkiTable = ({ data, onEdit, onDelete }: IPkiTableProps) => { return ( columnConfigs={tableConfig} - defaultSortHeader="org_name" + defaultSortHeader="pki_name" disableTableHeader disablePagination showMarkAllPages={false} diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx index 056396bb6daf..e6c2d2575b8c 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/PkiTableConfig.tsx @@ -9,7 +9,7 @@ import { IDropdownOption } from "interfaces/dropdownOption"; import HeaderCell from "components/TableContainer/DataTable/HeaderCell"; import ActionsDropdown from "components/ActionsDropdown"; import TextCell from "components/TableContainer/DataTable/TextCell"; -import StatusIndicatorWithIcon from "components/StatusIndicatorWithIcon"; +import Icon from "components/Icon"; type IPkiTableConfig = Column; type ITableStringCellProps = IStringCellProps; @@ -53,7 +53,11 @@ export const generateTableConfig = ( disableSortBy: true, Cell: ({ value: templates }: IPkiTemplatesCellProps) => { return templates.length ? ( - + // FIXME: See related note in frontend/components/StatusIndicatorWithIcon/StatusIndicatorWithIcon.tsx + + + Added + ) : ( ); diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss deleted file mode 100644 index 844f82633e0e..000000000000 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/PkiPage/components/PkiTable/_styles.scss +++ /dev/null @@ -1,2 +0,0 @@ -.pki-table { -} diff --git a/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx b/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx index 25f60e13ae6e..5edce5d0f60e 100644 --- a/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx +++ b/frontend/pages/admin/components/DownloadFileButtons/DownloadCSR.tsx @@ -3,15 +3,21 @@ import React, { FormEvent, useCallback, useMemo, useState } from "react"; import mdmAppleApi from "services/entities/mdm_apple"; import pkiApi from "services/entities/pki"; -import Icon from "components/Icon"; import Button from "components/buttons/Button"; +import TooltipWrapper from "components/TooltipWrapper"; +import Icon from "components/Icon"; + import { RequestState, downloadBase64ToFile } from "./helpers"; +// TODO: Refactor this interface to be more generalizable; probably some kind of fetchData +// callback that resolves to a base64 string and props for filename, button label, disable button, +// tooltip content, etc., and leave parms for the fetch (like pkiName) to the caller. interface IDownloadCSRProps { baseClass: string; onSuccess?: () => void; onError?: (e: unknown) => void; pkiName?: string; + disabled?: boolean; } const downloadCSRFile = (data: { csr: string }, filename?: string) => { @@ -53,6 +59,7 @@ const useDownloadCSR = ({ () => ({ downloadState, handleDownload, + isLoading: downloadState === "loading", }), [downloadState, handleDownload] ); @@ -65,20 +72,35 @@ export const DownloadCSR = ({ onSuccess, onError, pkiName, + disabled = false, }: IDownloadCSRProps) => { - const { handleDownload } = useDownloadCSR({ onSuccess, onError, pkiName }); + const { isLoading, handleDownload } = useDownloadCSR({ + onSuccess, + onError, + pkiName, + }); return ( - + + ); };