fleet/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditConfigurationModal/EditConfigurationModal.tsx
jacobshandling 0db86ef2f1
UI housekeeping: Update Modal.children from JSX.Element to React.ReactNode, remove empty fragment wrappers (#41394)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Simplified modal structures across multiple dialog components for
improved code maintainability.
* Enhanced modal component's flexibility to support broader content
types.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-10 15:30:55 -07:00

172 lines
4.4 KiB
TypeScript

import React, { useContext, useState } from "react";
import { IAppStoreApp } from "interfaces/software";
import { NotificationContext } from "context/notification";
import softwareAPI from "services/entities/software";
import Modal from "components/Modal";
import ModalFooter from "components/ModalFooter";
import Editor from "components/Editor";
import Button from "components/buttons/Button";
import CustomLink from "components/CustomLink";
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
import InstallerDetailsWidget from "../SoftwareInstallerCard/InstallerDetailsWidget";
import { getErrorMessage } from "./helpers";
const baseClass = "edit-configuration-modal";
// Used to surface error.message in UI of unknown error type
type ErrorWithMessage = {
message: string;
[key: string]: unknown;
};
const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {
return (error as ErrorWithMessage).message !== undefined;
};
export interface ISoftwareConfigurationFormData {
configuration: string;
}
interface EditConfigurationModal {
softwareId: number;
teamId: number;
softwareInstaller: IAppStoreApp;
refetchSoftwareTitle: () => void;
onExit: () => void;
}
const EditConfigurationModal = ({
softwareInstaller,
softwareId,
teamId,
refetchSoftwareTitle,
onExit,
}: EditConfigurationModal) => {
const { renderFlash } = useContext(NotificationContext);
const [isUpdatingConfiguration, setIsUpdatingConfiguration] = useState(false);
const [canSaveForm, setCanSaveForm] = useState(true);
const [jsonFormData, setJsonFormData] = useState<string>(
JSON.stringify(softwareInstaller.configuration, null, "\t") || "{}"
);
const [formError, setFormError] = useState<string | null>(null);
const validateForm = (curFormData: string) => {
let error = null;
if (curFormData) {
try {
JSON.parse(curFormData);
} catch (e: unknown) {
if (isErrorWithMessage(e)) {
error = e.message.toString();
} else {
throw e;
}
}
}
return error;
};
// Edit package API call
const onEditConfiguration = async (
evt: React.MouseEvent<HTMLFormElement>
) => {
setIsUpdatingConfiguration(true);
evt.preventDefault();
// Format for API
const formDataToSubmit =
jsonFormData === ""
? { configuration: {} } // Send empty object if no keys are set
: {
configuration: (jsonFormData && JSON.parse(jsonFormData)) || null,
};
try {
await softwareAPI.editAppStoreApp(softwareId, teamId, formDataToSubmit);
renderFlash(
"success",
<>
<strong>{softwareInstaller.name}</strong> configuration updated.
</>
);
refetchSoftwareTitle();
onExit();
} catch (e) {
renderFlash(
"error",
getErrorMessage(e, softwareInstaller as IAppStoreApp)
);
}
setIsUpdatingConfiguration(false);
};
const onInputChange = (value: string) => {
setJsonFormData(value);
const error = validateForm(value);
setFormError(error);
setCanSaveForm(!error);
};
const renderHelpText = () => {
return (
<div className={`${baseClass}__help-text`}>
The Android app&apos;s configuration in JSON format.{" "}
<CustomLink
newTab
text="Learn more"
url={`${LEARN_MORE_ABOUT_BASE_LINK}/android-software-managed-configuration`}
/>
</div>
);
};
const renderForm = () => (
<>
<Editor
mode="json"
value={jsonFormData as string}
helpText={renderHelpText()}
onChange={onInputChange}
error={formError}
label="Configuration"
/>
</>
);
return (
<Modal className={baseClass} title="Edit configuration" onExit={onExit}>
<InstallerDetailsWidget
softwareName={softwareInstaller.name}
androidPlayStoreId={softwareInstaller.app_store_id}
customDetails="Android"
installerType="app-store"
isFma={false}
isScriptPackage={false}
/>
{renderForm()}
<ModalFooter
primaryButtons={
<Button
type="submit"
onClick={onEditConfiguration}
isLoading={isUpdatingConfiguration}
disabled={!canSaveForm || isUpdatingConfiguration}
>
Save
</Button>
}
/>
</Modal>
);
};
export default EditConfigurationModal;