fleet/frontend/pages/DashboardPage/cards/ActivityFeed/components/ScriptBatchStatusTable/ScriptBatchStatusTableConfig.tsx
jacobshandling e25c1c3728
UI: Add ability to run a script on all hosts that match a set of supported filters; Add UI to view batch run summaries (#29025)
_Only merge to `main` after [back
end](https://github.com/fleetdm/fleet/pull/29149) and [back end
extension](https://github.com/fleetdm/fleet/pull/29312)_

## For #28699, #29143, #29281

- Run scripts by filter
- View batch script run summary via activity feed
- Code clean up

### Run scripts by filter:
<img width="1280" alt="Screenshot 2025-05-09 at 5 21 51 PM"
src="https://github.com/user-attachments/assets/bcf2e275-f229-461b-8411-0e99c34af5bf"
/>
<img width="1280" alt="Screenshot 2025-05-09 at 5 22 47 PM"
src="https://github.com/user-attachments/assets/d4882ed3-cfa6-4952-acbe-89c60d65d482"
/>

### View script run summary:

![ezgif-4ebaf9c57d6e57](https://github.com/user-attachments/assets/4201ff85-04e3-473f-8a82-969f85e59558)

- [x] Changes file added for user-visible changes in `changes/`
- [x] A detailed QA plan exists on the associated ticket (if it isn't
there, work with the product group's QA engineer to add it)
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2025-05-22 16:45:43 -07:00

119 lines
2.7 KiB
TypeScript

import React from "react";
import { Column } from "react-table";
import StatusIndicatorWithIcon from "components/StatusIndicatorWithIcon";
import {
INumberCellProps,
IStringCellProps,
} from "interfaces/datatable_config";
import { IScriptBatchSummaryResponse } from "services/entities/scripts";
import Button from "components/buttons/Button";
interface IHostCountCellProps {
status: string;
count: number;
onClickCancel: () => void;
}
const HostCountCell = ({
status,
count,
onClickCancel,
}: IHostCountCellProps) => {
const baseClass = "script-batch-status-host-count-cell";
return (
<div className={baseClass}>
<div>{count}</div>
{status === "pending" && (
<Button
className={`${baseClass}__cancel-button`}
onClick={onClickCancel}
variant="text-icon"
>
<span>Cancel</span>
</Button>
)}
</div>
);
};
type IStatus = "ran" | "pending" | "errored";
interface IRowData {
status: string;
hosts: number;
}
const STATUS_ORDER = ["ran", "pending", "errored"];
export interface IStatusCellValue {
displayName: string;
statusName: IStatus;
value: IStatus;
}
const STATUS_DISPLAY_OPTIONS = {
ran: {
displayName: "Ran",
indicatorStatus: "success",
},
pending: {
displayName: "Pending",
indicatorStatus: "pendingPartial",
},
errored: {
displayName: "Error",
indicatorStatus: "error",
},
} as const;
type IColumnConfig = Column<IRowData>;
type IStatusCellProps = IStringCellProps<IRowData>;
type IHostCellProps = INumberCellProps<IRowData>;
export const generateTableConfig = (
onClickCancel: () => void
): IColumnConfig[] => {
return [
{
Header: "Status",
disableSortBy: true,
accessor: "status",
Cell: ({ cell: { value } }: IStatusCellProps) => {
const statusOption =
STATUS_DISPLAY_OPTIONS[value as keyof typeof STATUS_DISPLAY_OPTIONS];
return (
<StatusIndicatorWithIcon
status={statusOption.indicatorStatus}
value={statusOption.displayName}
/>
);
},
},
{
Header: "Hosts",
accessor: "hosts",
disableSortBy: true,
Cell: ({ cell }: IHostCellProps) => {
return (
<HostCountCell
count={cell.value}
status={cell.row.original.status}
onClickCancel={onClickCancel}
/>
);
},
},
];
};
export const generateTableData = (
statusData: IScriptBatchSummaryResponse
): IRowData[] => {
const tableData = STATUS_ORDER.map((status) => ({
status,
hosts: statusData[status as keyof IScriptBatchSummaryResponse] as number,
}));
return tableData;
};