mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Resolves #42383. Re-roll of #42384 using the relevant helper function. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [ ] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Enhanced Android software configuration success notifications to dynamically display the actual software display name, replacing previously static messaging. This improvement provides users with more specific and personalized feedback when confirming successful software configurations, improving clarity and reducing potential confusion when managing multiple software installations or updates on their Android devices. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
179 lines
4.6 KiB
TypeScript
179 lines
4.6 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";
|
|
import { getDisplayedSoftwareName } 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>
|
|
{getDisplayedSoftwareName(
|
|
softwareInstaller.name,
|
|
softwareInstaller.display_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'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;
|