import React, { useContext, useState } from "react"; import classnames from "classnames"; import { ISoftwareTitleDetails, IAppStoreApp } from "interfaces/software"; import { ILabelSummary } from "interfaces/label"; import { useQuery } from "react-query"; import { NotificationContext } from "context/notification"; import useGitOpsMode from "hooks/useGitOpsMode"; import softwareAPI from "services/entities/software"; import labelsAPI, { getCustomLabels } from "services/entities/labels"; import Card from "components/Card"; import Modal from "components/Modal"; import ModalFooter from "components/ModalFooter"; import Checkbox from "components/forms/fields/Checkbox"; import TargetLabelSelector from "components/TargetLabelSelector"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { CUSTOM_TARGET_OPTIONS, generateSelectedLabels, getCustomTarget, getDisplayedSoftwareName, generateHelpText, getTargetType, } from "pages/SoftwarePage/helpers"; import InputField from "components/forms/fields/InputField"; import Button from "components/buttons/Button"; import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants"; import { ISoftwareAutoUpdateConfigFormValidation, ISoftwareAutoUpdateConfigInputValidation, validateFormData, } from "./helpers"; const baseClass = "edit-auto-update-config-modal"; const formClass = "edit-auto-update-config-form"; // Schema for the form data that will be used in the UI // and sent to the API. export interface ISoftwareAutoUpdateConfigFormData { autoUpdateEnabled: boolean; autoUpdateStartTime: string; autoUpdateEndTime: string; targetType: string; customTarget: string; labelTargets: Record; } interface EditAutoUpdateConfigModal { teamId: number; softwareTitle: ISoftwareTitleDetails; refetchSoftwareTitle: () => void; onExit: () => void; } const EditAutoUpdateConfigModal = ({ softwareTitle, teamId, refetchSoftwareTitle, onExit, }: EditAutoUpdateConfigModal) => { const { renderFlash } = useContext(NotificationContext); const { gitOpsModeEnabled } = useGitOpsMode("software"); const formClassNames = classnames(formClass, { [`edit-auto-update-config-form--disabled`]: gitOpsModeEnabled, }); const [isUpdatingConfiguration, setIsUpdatingConfiguration] = useState(false); const [formData, setFormData] = useState({ autoUpdateEnabled: softwareTitle.auto_update_enabled || false, autoUpdateStartTime: softwareTitle.auto_update_window_start || "", autoUpdateEndTime: softwareTitle.auto_update_window_end || "", targetType: getTargetType(softwareTitle.app_store_app as IAppStoreApp), customTarget: getCustomTarget(softwareTitle.app_store_app as IAppStoreApp), labelTargets: generateSelectedLabels( softwareTitle.app_store_app as IAppStoreApp ), }); // Fetch labels for TargetLabelSelector const { data: labels } = useQuery( ["custom_labels"], () => labelsAPI.summary(teamId).then((res) => getCustomLabels(res.labels)), { ...DEFAULT_USE_QUERY_OPTIONS, } ); const [ formValidation, setFormValidation, ] = useState(() => validateFormData(formData) ); // Currently calls the "edit app store app" API. // FUTURE: switch endpoint based on software title type? const onSubmitForm = async (evt: React.MouseEvent) => { evt.preventDefault(); const newValidation = validateFormData(formData, true); setFormValidation(newValidation); if (!newValidation.isValid) { return false; } setIsUpdatingConfiguration(true); try { await softwareAPI.editAppStoreApp(softwareTitle.id, teamId, formData); renderFlash( "success", <> {getDisplayedSoftwareName( softwareTitle.name, softwareTitle.display_name )} {" "} configuration updated. ); refetchSoftwareTitle(); onExit(); } catch (e) { renderFlash( "error", "An error occurred while updating the configuration. Please try again." ); } setIsUpdatingConfiguration(false); return true; }; const onToggleEnabled = (value: boolean) => { const newFormData = { ...formData, autoUpdateEnabled: value }; setFormData(newFormData); setFormValidation(validateFormData(newFormData)); }; const onChangeTimeField = (update: { name: string; value: string }) => { // Ensure HH:MM format with proper characters. const value = update.value.substring(0, 5).replace(/[^0-9:]/g, ""); const newFormData = { ...formData, [update.name]: value }; setFormData(newFormData); const newValidation = validateFormData(newFormData); // Can be "autoUpdateStartTime" or "autoUpdateEndTime". const fieldName = update.name as keyof ISoftwareAutoUpdateConfigFormValidation; const fieldValidation = newValidation[ fieldName ] as ISoftwareAutoUpdateConfigInputValidation; // We don't want to show an error message as the user types. // (that will happen on blur instead) // We'll just clear any existing error if the field is valid. if (fieldValidation?.isValid) { setFormValidation(newValidation); } }; const onSelectTargetType = (value: string) => { const newData = { ...formData, targetType: value }; setFormData(newData); setFormValidation(validateFormData(newData)); }; const onSelectCustomTargetOption = (value: string) => { const newData = { ...formData, customTarget: value }; setFormData(newData); setFormValidation(validateFormData(newData)); }; const onSelectLabel = ({ name, value }: { name: string; value: boolean }) => { const newData = { ...formData, labelTargets: { ...formData.labelTargets, [name]: value }, }; setFormData(newData); setFormValidation(validateFormData(newData)); }; const earliestStartTimeError = formValidation.autoUpdateStartTime?.message || (formValidation.windowLength?.message ? "Earliest start time" : undefined); const latestStartTimeError = formValidation.autoUpdateEndTime?.message || (formValidation.windowLength?.message ? "Latest start time" : undefined); const updateWindowLabel = formValidation.windowLength?.message || ( <>Update window (host’s local time) ); const updateWindowLabelClass = classnames("form-field__label", { "form-field__label--error": !!formValidation.windowLength?.message, }); return (
Auto updates
Automatically update{" "} {getDisplayedSoftwareName( softwareTitle.name, softwareTitle.display_name )} {" "} on all targeted hosts when a new version is available.
onToggleEnabled(newVal)} > Enable auto updates
{formData.autoUpdateEnabled && ( <>
{updateWindowLabel}
Times are formatted as HH:MM in 24 hour time (e.g., "13:37").
setFormValidation(validateFormData(formData)) } label="Earliest start time" name="autoUpdateStartTime" parseTarget error={earliestStartTimeError} /> setFormValidation(validateFormData(formData)) } label="Latest start time" name="autoUpdateEndTime" parseTarget error={latestStartTimeError} />
)}
( )} /> } />
); }; export default EditAutoUpdateConfigModal;