fleet/frontend/pages/queries/ManageQueriesPage/components/ManageQueryAutomationsModal/ManageQueryAutomationsModal.tsx
jacobshandling 19a64941ba
UI – Add VPP features for iPadOS and iOS (#20755)
## Addresses #20467 – part 2

### Aggregate software:

#### Software titles
<img width="1616" alt="sw-titles-updated"
src="https://github.com/user-attachments/assets/0b9922c7-e36e-4d2f-b204-95c3cdf9b602">

#### Software versions
<img width="1616" alt="Screenshot 2024-07-29 at 6 14 21 PM"
src="https://github.com/user-attachments/assets/5a097700-cd6c-45b1-a21f-9d76a733f0ae">

#### Host software
<img width="1616" alt="Screenshot 2024-07-29 at 6 23 01 PM"
src="https://github.com/user-attachments/assets/84e18695-f47a-4022-bd53-7f5d37ce452a">


### Add software modal (VPP) _screenshots use mocked data - UI is
flexible enough to display cleanly before and after backend is in
place:_
<img width="1339" alt="happy"
src="https://github.com/user-attachments/assets/8900aa93-316c-4a09-8e5a-1a1e45b0c458">

#### No apps:
<img width="1572" alt="Screenshot 2024-07-29 at 6 35 03 PM"
src="https://github.com/user-attachments/assets/466b9b6c-4d3d-49dd-94a9-94e395d89cb7">

#### Not enabled:
<img width="1572" alt="Screenshot 2024-07-29 at 6 37 45 PM"
src="https://github.com/user-attachments/assets/9bcfd480-8741-4d95-ba3b-550dee4dc673">

#### Error:
<img width="1572" alt="Screenshot 2024-07-29 at 6 39 39 PM"
src="https://github.com/user-attachments/assets/e944dd40-676e-4aba-9cd9-49ff319bf402">

### Vuln support – Not supported for now:
_see above screenshots for `list` endpoints_

#### Software title detail
<img width="1616" alt="Screenshot 2024-07-29 at 6 47 29 PM"
src="https://github.com/user-attachments/assets/2e30fd0a-21e4-4d19-bf9b-71a994bfd0e7">

#### Software version and OS detail:
<img width="1616" alt="Screenshot 2024-07-29 at 6 48 28 PM"
src="https://github.com/user-attachments/assets/e8fec769-ba97-4b6b-b10c-9bb4c973c732">
<img width="1616" alt="Screenshot 2024-07-29 at 6 50 25 PM"
src="https://github.com/user-attachments/assets/0ac15727-e0cb-447c-8758-c58b79656d1a">


- [x] Changes file added for user-visible changes in `changes/`,
- [x] Added/updated tests
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2024-07-30 10:14:25 -07:00

205 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
import InfoBanner from "components/InfoBanner/InfoBanner";
import CustomLink from "components/CustomLink/CustomLink";
import Checkbox from "components/forms/fields/Checkbox/Checkbox";
import QueryFrequencyIndicator from "components/QueryFrequencyIndicator/QueryFrequencyIndicator";
import LogDestinationIndicator from "components/LogDestinationIndicator/LogDestinationIndicator";
import { ISchedulableQuery } from "interfaces/schedulable_query";
import TooltipTruncatedText from "components/TooltipTruncatedText";
import { CONTACT_FLEET_LINK } from "utilities/constants";
interface IManageQueryAutomationsModalProps {
isUpdatingAutomations: boolean;
onSubmit: (formData: any) => void; // TODO
onCancel: () => void;
isShowingPreviewDataModal: boolean;
togglePreviewDataModal: () => void;
availableQueries?: ISchedulableQuery[];
automatedQueryIds: number[];
logDestination: string;
}
interface ICheckedQuery {
name?: string;
id: number;
isChecked: boolean;
interval: number;
}
const useCheckboxListStateManagement = (
allQueries: ISchedulableQuery[],
automatedQueryIds: number[] | undefined
) => {
const [queryItems, setQueryItems] = useState<ICheckedQuery[]>(() => {
return allQueries.map(({ name, id, interval }) => ({
name,
id,
isChecked: !!automatedQueryIds?.includes(id),
interval,
}));
});
const updateQueryItems = (queryId: number) => {
setQueryItems((prevItems) =>
prevItems.map((query) =>
query.id !== queryId ? query : { ...query, isChecked: !query.isChecked }
)
);
};
return { queryItems, updateQueryItems };
};
const baseClass = "manage-query-automations-modal";
const ManageQueryAutomationsModal = ({
isUpdatingAutomations,
automatedQueryIds,
onSubmit,
onCancel,
isShowingPreviewDataModal,
togglePreviewDataModal,
availableQueries,
logDestination,
}: IManageQueryAutomationsModalProps): JSX.Element => {
// TODO: Error handling, if any
// const [errors, setErrors] = useState<{ [key: string]: string }>({});
// Client side sort queries alphabetically
const sortedAvailableQueries =
availableQueries?.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
) || [];
const { queryItems, updateQueryItems } = useCheckboxListStateManagement(
sortedAvailableQueries,
automatedQueryIds || []
);
const onSubmitQueryAutomations = (
evt: React.MouseEvent<HTMLFormElement> | KeyboardEvent
) => {
evt.preventDefault();
const newQueryIds: number[] = [];
queryItems?.forEach((p) => p.isChecked && newQueryIds.push(p.id));
onSubmit(newQueryIds);
};
useEffect(() => {
const listener = (event: KeyboardEvent) => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
event.preventDefault();
onSubmit(event);
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
});
return (
<Modal
title="Manage automations"
onExit={onCancel}
className={baseClass}
width="large"
isHidden={isShowingPreviewDataModal}
>
<div className={`${baseClass} form`}>
<div className={`${baseClass}__heading`}>
Query automations let you send data to your log destination on a
schedule. Data is sent according to a querys frequency.
</div>
{availableQueries?.length ? (
<div className={`${baseClass}__select form-field`}>
<div className="form-field__label">
Choose which queries will send data:
</div>
<div className={`${baseClass}__checkboxes`}>
{queryItems &&
queryItems.map((queryItem) => {
const { isChecked, name, id, interval } = queryItem;
return (
<div key={id} className={`${baseClass}__query-item`}>
<Checkbox
value={isChecked}
name={name}
onChange={() => {
updateQueryItems(id);
// !isChecked &&
// setErrors((errs) => omit(errs, "queryItems"));
}}
>
<TooltipTruncatedText value={name} />
</Checkbox>
<QueryFrequencyIndicator
frequency={interval}
checked={isChecked}
/>
</div>
);
})}
</div>
</div>
) : (
<div className={`${baseClass}__no-queries`}>
<b>You have no queries.</b>
<p>Add a query to turn on automations.</p>
</div>
)}
<div className={`${baseClass}__log-destination form-field`}>
<div className="form-field__label">Log destination:</div>
<div className={`${baseClass}__selection`}>
<LogDestinationIndicator logDestination={logDestination} />
</div>
<div className={`${baseClass}__configure form-field__help-text`}>
Users with the admin role can&nbsp;
<CustomLink
url="https://fleetdm.com/docs/using-fleet/log-destinations"
text="configure a different log destination"
newTab
/>
</div>
</div>
<InfoBanner className={`${baseClass}__supported-platforms`}>
<p>Automations currently run on macOS, Windows, and Linux hosts.</p>
<p>
Interested in query automations for your Chromebooks? &nbsp;
<CustomLink url={CONTACT_FLEET_LINK} text="Let us know" newTab />
</p>
</InfoBanner>
<Button
type="button"
variant="text-link"
onClick={togglePreviewDataModal}
className={`${baseClass}__preview-data`}
>
Preview data
</Button>
<div className="modal-cta-wrap">
<Button
type="submit"
variant="brand"
onClick={onSubmitQueryAutomations}
className="save-loading"
isLoading={isUpdatingAutomations}
>
Save
</Button>
<Button onClick={onCancel} variant="inverse">
Cancel
</Button>
</div>
</div>
</Modal>
);
};
export default ManageQueryAutomationsModal;