Skip to content

add Manage Automations modal - #46254

Merged
nulmete merged 6 commits into
policy-automations-continuous-retriesfrom
45148-manage-automations-frontend-new-manage
May 29, 2026
Merged

add Manage Automations modal#46254
nulmete merged 6 commits into
policy-automations-continuous-retriesfrom
45148-manage-automations-frontend-new-manage

Conversation

@nulmete

@nulmete nulmete commented May 27, 2026

Copy link
Copy Markdown
Member

Related issue: Resolves #45148 (just the modal itself, Edit policy and Save policy modal changes will be follow-ups).

Figma: https://www.figma.com/design/QeOcex5LtuDYG9XrGnMFLZ/-42651-Easier-to-manage-policy-automations-with-continuous-retry-option-for-software-scripts?node-id=5373-9851&t=U4xhTwwGzJ9osxYF-0

Testing

  • QA'd all new/changed functionality manually
Screen.Recording.2026-05-27.at.12.18.57.PM.mov

Summary by CodeRabbit

  • New Features
    • Implemented a fully functional policy automations management modal with support for multiple automation types, including ticket/webhook integration, software installation, script execution, calendar events, conditional access, and continuous automations
    • Added dynamic software and script selection with platform-specific filtering for policy configurations

Review Change Stack

@@ -1,29 +1,422 @@
import React from "react";
/* eslint-disable @typescript-eslint/no-use-before-define */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to resolve an issue when using the AutomationRowTooltip within ManageAutomationsModal, which is at the bottom of the file.
I prefer having the "main" component (ManageAutomationsModal) at the top and any other inner components at the bottom (treat them as private functions / implementation details).

I don't think it's worth it to move it to a separate file (yet) but it has enough logic that it's worth being its own function/component IMHO.
I have a draft PR proposing this pattern instead of calling render*() functions within the main component: #46213

@nulmete
nulmete marked this pull request as ready for review May 27, 2026 14:26
@nulmete
nulmete requested a review from a team as a code owner May 27, 2026 14:26

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@nulmete
nulmete marked this pull request as draft May 27, 2026 14:27
@codecov

codecov Bot commented May 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 5.71429% with 165 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.79%. Comparing base (73810d4) to head (72dc3b6).

Files with missing lines Patch % Lines
.../ManageAutomationsModal/ManageAutomationsModal.tsx 2.47% 118 Missing ⚠️
...tomationsModal/hooks/useUpdatePolicyAutomations.ts 3.57% 27 Missing ⚠️
...sPage/components/ManageAutomationsModal/helpers.ts 12.50% 14 Missing ⚠️
...policies/ManagePoliciesPage/ManagePoliciesPage.tsx 0.00% 2 Missing ⚠️
...ponents/ManageAutomationsModal/hooks/useScripts.ts 50.00% 2 Missing ⚠️
.../ManageAutomationsModal/hooks/useSoftwareTitles.ts 50.00% 2 Missing ⚠️
Additional details and impacted files
@@                            Coverage Diff                            @@
##           policy-automations-continuous-retries   #46254      +/-   ##
=========================================================================
- Coverage                                  66.84%   66.79%   -0.05%     
=========================================================================
  Files                                       2758     2762       +4     
  Lines                                     219781   219956     +175     
  Branches                                   11048    11004      -44     
=========================================================================
+ Hits                                      146903   146913      +10     
- Misses                                     59608    59774     +166     
+ Partials                                   13270    13269       -1     
Flag Coverage Δ
frontend 55.70% <5.71%> (-0.30%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…tries' into 45148-manage-automations-frontend-new-manage
@nulmete
nulmete marked this pull request as ready for review May 27, 2026 15:20
@nulmete

nulmete commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR implements a complete modal UI for managing policy automations, starting with a new continuous_automations_enabled field added to the policy form data model and API layer. The implementation includes custom hooks for fetching scripts and software titles, a useUpdatePolicyAutomations mutation hook that conditionally persists both per-policy settings and webhook/ticket membership changes, TypeScript types for automation row shapes, helper functions for ticket/webhook state logic, and a fully-featured ManageAutomationsModal component with dynamic automation rows, optional dropdown pickers, and validation. The modal integrates into the policies management page to allow per-policy automation configuration.

Possibly related PRs

  • fleetdm/fleet#45999: Adds backend support for the continuous_automations_enabled policy field and its continuous-automation behavior, directly paired with this PR's frontend addition of that field through the automation UI and update payloads.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description is minimal and missing required checklist items, though QA was confirmed completed. Add the standard PR checklist sections: Changes files, Security/validation, Testing (automated tests), Database migrations (if applicable), and any fleetd/orbit-related verifications. Provide more detail on testing approach and scope.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a new Manage Automations modal component to the policies page.
Linked Issues check ✅ Passed The PR implements the frontend Manage Automations modal as specified in issue #45148, including modal UI with automation toggles, configuration options, and Save functionality.
Out of Scope Changes check ✅ Passed All changes are focused on implementing the Manage Automations modal and related infrastructure; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 45148-manage-automations-frontend-new-manage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/types.ts (1)

3-8: ⚡ Quick win

Consider exporting IAutomationRowKey for better reusability.

While the key type is accessible via IAutomationRow['key'], directly exporting IAutomationRowKey would improve developer ergonomics for switch statements, type guards, or pattern matching on automation keys outside this module.

📤 Proposed change to export the type
-type IAutomationRowKey =
+export type IAutomationRowKey =
   | "ticket_webhook"
   | "install_software"
   | "run_script"
   | "calendar_event"
   | "conditional_access";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/types.ts`
around lines 3 - 8, The type IAutomationRowKey is currently internal and should
be exported so other modules can reuse it; update the type declaration for
IAutomationRowKey to be exported (export type IAutomationRowKey = ...) and
ensure any existing imports or usages reference the exported name (e.g., where
IAutomationRow['key'] is used, prefer importing IAutomationRowKey where
appropriate) to enable clearer switch statements and type guards across the
codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/useUpdatePolicyAutomations.ts`:
- Around line 30-37: Guard against missing teamIdForApi for team-scoped updates:
in the hook (useUpdatePolicyAutomations) add an early validation that if
isGlobalPolicy is false and teamIdForApi is undefined, abort the update (call
onError if provided and return) before performing the API call or cache writes;
only include teamIdForApi in the request payload and in any cache key/path
updates when it is defined, and ensure this guard is placed before the existing
logic that references teamIdForApi (the code around the update call and cache
mutations that currently use teamIdForApi at the referenced lines).

---

Nitpick comments:
In
`@frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/types.ts`:
- Around line 3-8: The type IAutomationRowKey is currently internal and should
be exported so other modules can reuse it; update the type declaration for
IAutomationRowKey to be exported (export type IAutomationRowKey = ...) and
ensure any existing imports or usages reference the exported name (e.g., where
IAutomationRow['key'] is used, prefer importing IAutomationRowKey where
appropriate) to enable clearer switch statements and type guards across the
codebase.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46817ea6-5530-4751-b934-75e1f3d10ef3

📥 Commits

Reviewing files that changed from the base of the PR and between 73810d4 and 3474a21.

📒 Files selected for processing (11)
  • frontend/interfaces/policy.ts
  • frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/_styles.scss
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/helpers.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/index.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/useScripts.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/useSoftwareTitles.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/useUpdatePolicyAutomations.ts
  • frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/types.ts
  • frontend/services/entities/team_policies.ts

Comment on lines +30 to +37
interface IUseUpdatePolicyAutomationsArgs {
policy: IPolicyStats;
teamIdForApi: number | undefined;
isGlobalPolicy: boolean;
automationsConfig: IConfig | ITeamConfig | undefined;
onSuccess?: () => void;
onError?: () => void;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard team-scoped updates when teamIdForApi is missing.

For non-global policies, teamIdForApi is still optional by type and is used directly at Line 73 and Line 74 (and passed in payload at Line 84). If it’s undefined, updates and cache writes target an invalid key/path.

Suggested hardening
 const useUpdatePolicyAutomations = ({
   policy,
   teamIdForApi,
   isGlobalPolicy,
   automationsConfig,
   onSuccess,
   onError,
 }: IUseUpdatePolicyAutomationsArgs) => {
   const queryClient = useQueryClient();
   const { setConfig } = useContext(AppContext);
+
+  if (!isGlobalPolicy && teamIdForApi === undefined) {
+    throw new Error(
+      "teamIdForApi is required for team-scoped policy automation updates."
+    );
+  }
@@
     if (isGlobalPolicy) {
       const updatedConfig = await configAPI.update(payload);
       queryClient.setQueryData(["config"], updatedConfig);
       setConfig(updatedConfig);
     } else {
-      const updatedTeam = await teamsAPI.update(payload, teamIdForApi);
-      queryClient.setQueryData(["teams", teamIdForApi], updatedTeam);
+      const updatedTeam = await teamsAPI.update(payload, teamIdForApi!);
+      queryClient.setQueryData(["teams", teamIdForApi!], updatedTeam);
     }
   };

Also applies to: 68-75, 83-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/hooks/useUpdatePolicyAutomations.ts`
around lines 30 - 37, Guard against missing teamIdForApi for team-scoped
updates: in the hook (useUpdatePolicyAutomations) add an early validation that
if isGlobalPolicy is false and teamIdForApi is undefined, abort the update (call
onError if provided and return) before performing the API call or cache writes;
only include teamIdForApi in the request payload and in any cache key/path
updates when it is defined, and ensure this guard is placed before the existing
logic that references teamIdForApi (the code around the update call and cache
mutations that currently use teamIdForApi at the referenced lines).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in f9e6096

const SCRIPTS_PAGE_SIZE = 1000;

interface IUseScriptsArgs {
teamId: number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fleetId

<ManageAutomationsModal
policy={selectedPolicyForAutomations}
fleetName={
isAllTeamsSelected ? "All fleets" : currentTeamSummary?.name ?? ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the fleetName values should only constrained to either the fleet name or "All fleets" I don't think this will do the right thing if the user has "Unassigned" selected?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually it does set "Unassigned" correctly, however I simplified to:

fleetName={currentTeamSummary?.name ?? ""}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point was that I don't think we want to display "Unassigned" in this context, AFAIK, when talking about policies either we have global policies (which should render "All fleets") or Fleet policies (which should display the fleet name) - but I see this was fixed in https://github.com/fleetdm/fleet/pull/46271/changes#diff-26bb41183dde00948e910199094c5796925e3219348dd9a9d6a7c8042e87d55bR997

)}
</div>

<div className="modal-cta-wrap">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is styled using row-reverse, so we actually need the Save button to come before the Cancel button.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed in 72dc3b6

const SOFTWARE_PAGE_SIZE = 1000;

interface IUseSoftwareTitlesArgs {
teamId: number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

? {
software_title_id: installSoftware ? softwareTitleId : null,
script_id: runScript ? scriptId : null,
calendar_events_enabled: calendarEvent,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need to also check whether the calendar events are enabled for the team before setting this? isCalendarEnabledForTeam?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right 👍 - addressed in 72dc3b6

software_title_id: installSoftware ? softwareTitleId : null,
script_id: runScript ? scriptId : null,
calendar_events_enabled: calendarEvent,
conditional_access_enabled: conditionalAccess,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, you might also want to check whether cond access is enabled for the team before persisting this isConditionalAccessEnabledForTeam

@nulmete
nulmete merged commit c083625 into policy-automations-continuous-retries May 29, 2026
16 checks passed
@nulmete
nulmete deleted the 45148-manage-automations-frontend-new-manage branch May 29, 2026 14:57
nulmete added a commit that referenced this pull request May 29, 2026
…rited policies (#46271)

**Related issue:** #45148 and #45145 

## Summary
Stacked on top of the Manage automations modal PR (#46254). Two
follow-up tweaks:

- **Open the modal from the empty ("---") automations cell.** Policies
with no automations configured were previously a non-interactive cell.
Now, for users who can edit, the empty cell is clickable.
- **Handle inherited policies opened from a fleet's list.** An inherited
policy has `team_id === null` even when viewed from within a specific
fleet. It's now treated as global (only the webhook/ticket row, no
continuous-retry option) and the modal is routed to the **global**
config for its automations, instead of inferring "global" from the
selected-team view.

## Testing

- [x] QA'd all new/changed functionality manually



https://github.com/user-attachments/assets/9a927373-e1d2-4326-92cc-a97f26d85962



https://github.com/user-attachments/assets/9ff9c393-56c7-4631-9052-477a8736af7a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants