diff --git a/changes/issue-6666-loading-spinners b/changes/issue-6666-loading-spinners
new file mode 100644
index 0000000000..c069890716
--- /dev/null
+++ b/changes/issue-6666-loading-spinners
@@ -0,0 +1 @@
+* Adds loading spinners to all buttons that post to the API
\ No newline at end of file
diff --git a/cypress/integration/all/app/fleetdesktop.spec.ts b/cypress/integration/all/app/fleetdesktop.spec.ts
index 08920e9405..3fd6967996 100644
--- a/cypress/integration/all/app/fleetdesktop.spec.ts
+++ b/cypress/integration/all/app/fleetdesktop.spec.ts
@@ -35,7 +35,7 @@ describe("Fleet Desktop", () => {
cy.getAttached('img[alt="Host info icon"]').click();
});
cy.getAttached(".device-user-info__modal").within(() => {
- cy.getAttached(".device-user-info__btn").click();
+ cy.findByRole("button", { name: /ok/i }).click();
});
});
it("renders and searches the host's software", () => {
diff --git a/cypress/integration/all/app/manageUsers.spec.ts b/cypress/integration/all/app/manageUsers.spec.ts
index bd0e3c0f5a..0a063cba48 100644
--- a/cypress/integration/all/app/manageUsers.spec.ts
+++ b/cypress/integration/all/app/manageUsers.spec.ts
@@ -50,28 +50,28 @@ describe("Manage users flow", () => {
}
);
cy.findByPlaceholderText("Password").clear().type(BAD_PASSWORD_LENGTH);
- cy.getAttached(".create-user-form__btn-wrap")
+ cy.getAttached(".modal-cta-wrap")
.contains("button", /create/i)
.click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(BAD_PASSWORD_NO_NUMBER);
- cy.getAttached(".create-user-form__btn-wrap")
+ cy.getAttached(".modal-cta-wrap")
.contains("button", /create/i)
.click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(BAD_PASSWORD_NO_NUMBER);
- cy.getAttached(".create-user-form__btn-wrap")
+ cy.getAttached(".modal-cta-wrap")
.contains("button", /create/i)
.click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(GOOD_PASSWORD);
- cy.getAttached(".create-user-form__btn-wrap")
+ cy.getAttached(".modal-cta-wrap")
.contains("button", /create/i)
.click();
cy.findByText(/new name/i).should("exist");
@@ -95,30 +95,22 @@ describe("Manage users flow", () => {
}
);
cy.findByLabelText("Password").clear().type(BAD_PASSWORD_LENGTH);
- cy.getAttached(".create-user-form__btn-wrap")
- .contains("button", /save/i)
- .click();
+ cy.getAttached(".modal-cta-wrap").contains("button", /save/i).click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(BAD_PASSWORD_NO_NUMBER);
- cy.getAttached(".create-user-form__btn-wrap")
- .contains("button", /save/i)
- .click();
+ cy.getAttached(".modal-cta-wrap").contains("button", /save/i).click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(BAD_PASSWORD_NO_SYMBOL);
- cy.getAttached(".create-user-form__btn-wrap")
- .contains("button", /save/i)
- .click();
+ cy.getAttached(".modal-cta-wrap").contains("button", /save/i).click();
cy.findByText(/password must meet the criteria below/i).should("exist");
cy.findByLabelText(/password must meet the criteria below/i)
.clear()
.type(GOOD_PASSWORD);
- cy.getAttached(".create-user-form__btn-wrap")
- .contains("button", /save/i)
- .click();
+ cy.getAttached(".modal-cta-wrap").contains("button", /save/i).click();
cy.findByText(/successfully edited/i).should("exist");
});
it("deletes an existing user", () => {
@@ -129,7 +121,7 @@ describe("Manage users flow", () => {
cy.findByText(/action/i).click();
cy.findByText(/delete/i).click();
});
- cy.getAttached(".delete-user-form__btn-wrap")
+ cy.getAttached(".modal-cta-wrap")
.contains("button", /delete/i)
.click();
cy.findByText(/successfully deleted/i).should("exist");
diff --git a/cypress/integration/all/app/policiesflow.spec.ts b/cypress/integration/all/app/policiesflow.spec.ts
index cfa7a0a72d..559f1b0b23 100644
--- a/cypress/integration/all/app/policiesflow.spec.ts
+++ b/cypress/integration/all/app/policiesflow.spec.ts
@@ -151,7 +151,7 @@ describe("Policies flow (empty)", () => {
.type(
"{selectall}SELECT 1 FROM users WHERE username = 'backup' LIMIT 1;"
);
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
cy.getAttached(".policy-form__policy-save-modal-name")
.click()
.type("Does the device have a user named 'backup'?");
@@ -161,7 +161,7 @@ describe("Policies flow (empty)", () => {
cy.getAttached(".policy-form__policy-save-modal-resolution")
.click()
.type("Create a user named 'backup'");
- cy.getAttached(".policy-form__button--modal-save").click();
+ cy.findByRole("button", { name: /save policy/i }).click();
cy.findByText(/policy created/i).should("exist");
});
@@ -170,10 +170,8 @@ describe("Policies flow (empty)", () => {
cy.findByText(/add a policy/i).click();
});
cy.findByText(/gatekeeper enabled/i).click();
- cy.getAttached(".policy-form__save").click();
- cy.getAttached(".policy-form__button-wrap--modal").within(() => {
- cy.getAttached(".policy-form__button--modal-save").click();
- });
+ cy.findByRole("button", { name: /save/i }).click();
+ cy.findByRole("button", { name: /save policy/i }).click();
cy.findByText(/policy created/i).should("exist");
});
});
@@ -215,7 +213,7 @@ describe("Policies flow (empty)", () => {
cy.getAttached(".manage-policies-page__header-wrap").within(() => {
cy.findByText(/add a policy/i).click();
});
- cy.getAttached(".add-policy-modal__modal").within(() => {
+ cy.getAttached(".add-policy-modal").within(() => {
cy.findByRole("button", { name: /create your own policy/i }).click();
});
@@ -304,7 +302,7 @@ describe("Policies flow (empty)", () => {
cy.getAttached(".manage-policies-page__header-wrap").within(() => {
cy.findByText(/add a policy/i).click();
});
- cy.getAttached(".add-policy-modal__modal").within(() => {
+ cy.getAttached(".add-policy-modal").within(() => {
cy.findByText("Automatic login disabled (macOS)").click();
});
@@ -313,7 +311,7 @@ describe("Policies flow (empty)", () => {
testCompatibility(el, i, [true, false, false]);
});
});
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
cy.getAttached(".platform-selector").within(() => {
cy.getAttached(".fleet-checkbox__input").each((el, i) => {
@@ -326,10 +324,10 @@ describe("Policies flow (empty)", () => {
cy.getAttached(".manage-policies-page__header-wrap").within(() => {
cy.findByText(/add a policy/i).click();
});
- cy.getAttached(".add-policy-modal__modal").within(() => {
+ cy.getAttached(".add-policy-modal").within(() => {
cy.findByText("Automatic login disabled (macOS)").click();
});
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
cy.getAttached(".platform-selector").within(() => {
cy.getAttached(".fleet-checkbox__input").each((el, i) => {
@@ -340,14 +338,16 @@ describe("Policies flow (empty)", () => {
testSelections(el, i, [false, false, false]);
});
});
- cy.getAttached(".policy-form__button--modal-save").should("be.disabled");
+ cy.getAttached(".modal-cta-wrap").within(() => {
+ cy.findByRole("button", { name: /save policy/i }).should("be.disabled");
+ });
});
it("allows user to overide preselected platforms when saving new policy", () => {
cy.getAttached(".manage-policies-page__header-wrap").within(() => {
cy.findByText(/add a policy/i).click();
});
- cy.getAttached(".add-policy-modal__modal").within(() => {
+ cy.getAttached(".add-policy-modal").within(() => {
cy.findByText("Automatic login disabled (macOS)").click();
});
@@ -356,7 +356,7 @@ describe("Policies flow (empty)", () => {
testCompatibility(el, i, [true, false, false]);
});
});
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
cy.getAttached(".platform-selector").within(() => {
cy.getAttached(".fleet-checkbox__input").each((el, i) => {
@@ -368,7 +368,7 @@ describe("Policies flow (empty)", () => {
testSelections(el, i, [false, false, true]);
});
});
- cy.getAttached(".policy-form__button--modal-save").click();
+ cy.findByRole("button", { name: /save policy/i }).click();
cy.findByText(/policy created/i).should("exist");
// confirm that new policy was saved with user-selected platforms
@@ -390,11 +390,11 @@ describe("Policies flow (empty)", () => {
cy.getAttached(".manage-policies-page__header-wrap").within(() => {
cy.findByText(/add a policy/i).click();
});
- cy.getAttached(".add-policy-modal__modal").within(() => {
+ cy.getAttached(".add-policy-modal").within(() => {
cy.findByText("Antivirus healthy (macOS)").click();
});
- cy.getAttached(".policy-form__save").click();
- cy.getAttached(".policy-form__button--modal-save").click();
+ cy.findByRole("button", { name: /save/i }).click();
+ cy.findByRole("button", { name: /save policy/i }).click();
cy.findByText(/policy created/i).should("exist");
// edit platform selections for policy
@@ -485,7 +485,7 @@ describe("Policies flow (seeded)", () => {
"{selectall}SELECT 1 FROM gatekeeper WHERE assessments_enabled = 1;"
);
cy.getAttached(".fleet-checkbox__label").first().click();
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
cy.findByText(/policy updated/i).should("exist");
cy.visit("policies/1");
cy.getAttached(".fleet-checkbox__input").first().should("not.be.checked");
@@ -500,7 +500,7 @@ describe("Policies flow (seeded)", () => {
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /cancel/i }).should("exist");
cy.findByRole("button", { name: /delete/i }).click();
});
@@ -530,6 +530,8 @@ describe("Policies flow (seeded)", () => {
cy.getAttached(".fleet-slider").click();
});
cy.findByRole("button", { name: /^Save$/ }).click();
+ // Confirm failing policies webhook was updated successfully
+ cy.findByText(/updated policy automations/i).should("exist");
});
it("empty automation state prompts to create an integration", () => {
cy.getAttached(".button-wrap").within(() => {
diff --git a/cypress/integration/all/app/queryflow.spec.ts b/cypress/integration/all/app/queryflow.spec.ts
index 9aec747b47..b16ca541f5 100644
--- a/cypress/integration/all/app/queryflow.spec.ts
+++ b/cypress/integration/all/app/queryflow.spec.ts
@@ -84,7 +84,7 @@ describe("Query flow (seeded)", () => {
cy.getAttached(".ace_scroller")
.click()
.type("{selectall}SELECT datetime, username FROM windows_crashes;");
- cy.getAttached(".query-form__save").click();
+ cy.findByRole("button", { name: "Save" }).click();
cy.findByText(/query updated/i).should("be.visible");
});
it("saves an existing query as new query", () => {
@@ -97,11 +97,12 @@ describe("Query flow (seeded)", () => {
cy.getAttached(".ace_scroller")
.click()
.type("{selectall}SELECT datetime, username FROM windows_crashes;");
- cy.getAttached(".query-form__save-as-new").click();
+ cy.findByRole("button", { name: /save as new/i }).click();
cy.findByText(/copy of/i).should("be.visible");
});
it("deletes an existing query", () => {
cy.findByText(/detect presence of authorized ssh keys/i)
+ .parent()
.parent()
.parent()
.within(() => {
@@ -151,9 +152,7 @@ describe("Query flow (seeded)", () => {
cy.getAttached(".input-field").click().type("50");
}
);
- cy.getAttached(
- ".schedule-editor-modal__btn-wrap .modal-cta-wrap"
- ).within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
@@ -171,7 +170,7 @@ describe("Query flow (seeded)", () => {
cy.findByText(/every 6 hours/i).click();
cy.findByText(/every day/i).click();
- cy.getAttached(".schedule-editor-modal__btn-wrap").within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
diff --git a/cypress/integration/free/admin.spec.ts b/cypress/integration/free/admin.spec.ts
index 36d07eaafe..ff234abf40 100644
--- a/cypress/integration/free/admin.spec.ts
+++ b/cypress/integration/free/admin.spec.ts
@@ -279,7 +279,7 @@ describe(
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
diff --git a/cypress/integration/free/maintainer.spec.ts b/cypress/integration/free/maintainer.spec.ts
index 4b77500d44..9b5dae075b 100644
--- a/cypress/integration/free/maintainer.spec.ts
+++ b/cypress/integration/free/maintainer.spec.ts
@@ -275,7 +275,7 @@ describe(
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
diff --git a/cypress/integration/premium/admin.spec.ts b/cypress/integration/premium/admin.spec.ts
index bbcd7faaf2..133048c2d8 100644
--- a/cypress/integration/premium/admin.spec.ts
+++ b/cypress/integration/premium/admin.spec.ts
@@ -479,9 +479,11 @@ describe("Premium tier - Global Admin user", () => {
cy.findByText(/gatekeeper enabled/i).click();
cy.getAttached(".policy-form__button-wrap").within(() => {
cy.findByRole("button", { name: /run/i }).should("exist");
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
+ });
+ cy.getAttached(".modal-cta-wrap").within(() => {
+ cy.findByRole("button", { name: /save policy/i }).click();
});
- cy.getAttached(".policy-form__button--modal-save").click();
cy.findByText(/policy created/i).should("exist");
cy.findByText(/gatekeeper enabled/i).should("exist");
});
@@ -539,7 +541,7 @@ describe("Premium tier - Global Admin user", () => {
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
@@ -593,7 +595,7 @@ describe("Premium tier - Global Admin user", () => {
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
diff --git a/cypress/integration/premium/maintainer.spec.ts b/cypress/integration/premium/maintainer.spec.ts
index 32e0985716..a68354d2b9 100644
--- a/cypress/integration/premium/maintainer.spec.ts
+++ b/cypress/integration/premium/maintainer.spec.ts
@@ -237,9 +237,11 @@ describe("Premium tier - Maintainer user", () => {
cy.findByText(/gatekeeper enabled/i).click();
cy.getAttached(".policy-form__button-wrap").within(() => {
cy.findByRole("button", { name: /run/i }).should("exist");
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
+ });
+ cy.getAttached(".modal-cta-wrap").within(() => {
+ cy.findByRole("button", { name: /save policy/i }).click();
});
- cy.getAttached(".policy-form__button--modal-save").click();
cy.findByText(/policy created/i).should("exist");
});
it("allows global maintainer to delete a team policy", () => {
@@ -260,7 +262,7 @@ describe("Premium tier - Maintainer user", () => {
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
diff --git a/cypress/integration/premium/team_admin.spec.ts b/cypress/integration/premium/team_admin.spec.ts
index f0d1342077..0424a191f5 100644
--- a/cypress/integration/premium/team_admin.spec.ts
+++ b/cypress/integration/premium/team_admin.spec.ts
@@ -223,7 +223,7 @@ describe("Premium tier - Team Admin user", () => {
});
cy.findAllByText(/detect presence/i).click();
});
- cy.getAttached(".query-form__save").should("be.disabled");
+ cy.findByRole("button", { name: "Save" }).should("be.disabled");
});
});
describe("Manage schedules page", () => {
@@ -242,7 +242,7 @@ describe("Premium tier - Team Admin user", () => {
cy.getAttached(".schedule-editor-modal__form").within(() => {
cy.findByText(/select query/i).click();
cy.findByText(/detect presence/i).click();
- cy.getAttached(".schedule-editor-modal__btn-wrap").within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
@@ -260,7 +260,7 @@ describe("Premium tier - Team Admin user", () => {
cy.findByText(/every day/i).click();
cy.findByText(/every 6 hours/i).click();
- cy.getAttached(".schedule-editor-modal__btn-wrap").within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
@@ -297,9 +297,11 @@ describe("Premium tier - Team Admin user", () => {
cy.findByText(/gatekeeper enabled/i).click();
cy.getAttached(".policy-form__button-wrap").within(() => {
cy.findByRole("button", { name: /run/i }).should("exist");
- cy.getAttached(".policy-form__save").click();
+ cy.findByRole("button", { name: /save/i }).click();
+ });
+ cy.getAttached(".modal-cta-wrap").within(() => {
+ cy.findByRole("button", { name: /save policy/i }).click();
});
- cy.getAttached(".policy-form__button--modal-save").click();
cy.findByText(/policy created/i).should("exist");
});
it("allows team admin to edit a team policy", () => {
@@ -345,7 +347,7 @@ describe("Premium tier - Team Admin user", () => {
});
});
cy.findByRole("button", { name: /delete/i }).click();
- cy.getAttached(".delete-policies-modal").within(() => {
+ cy.getAttached(".delete-policy-modal").within(() => {
cy.findByRole("button", { name: /delete/i }).should("exist");
cy.findByRole("button", { name: /cancel/i }).click();
});
diff --git a/cypress/integration/premium/team_maintainer_observer.spec.ts b/cypress/integration/premium/team_maintainer_observer.spec.ts
index 0f1e72c871..7e5c351c6e 100644
--- a/cypress/integration/premium/team_maintainer_observer.spec.ts
+++ b/cypress/integration/premium/team_maintainer_observer.spec.ts
@@ -263,7 +263,7 @@ describe("Premium tier - Team observer/maintainer user", () => {
cy.findByText(/detect presence/i).click();
cy.findByText(/every day/i).click();
cy.findByText(/every 6 hours/i).click();
- cy.getAttached(".schedule-editor-modal__btn-wrap").within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
@@ -285,8 +285,10 @@ describe("Premium tier - Team observer/maintainer user", () => {
// Add a default policy
cy.findByText(/gatekeeper enabled/i).click();
- cy.getAttached(".policy-form__save").click();
- cy.getAttached(".policy-form__button--modal-save").click();
+ cy.findByRole("button", { name: /save/i }).click();
+ cy.getAttached(".modal-cta-wrap").within(() => {
+ cy.findByRole("button", { name: /save policy/i }).click();
+ });
cy.findByText(/policy created/i).should("exist");
// On maintaining team, should see "save" and "run" for a new policy
diff --git a/cypress/integration/premium/teamflow.spec.ts b/cypress/integration/premium/teamflow.spec.ts
index fa37bc8b83..903948896c 100644
--- a/cypress/integration/premium/teamflow.spec.ts
+++ b/cypress/integration/premium/teamflow.spec.ts
@@ -120,7 +120,7 @@ describe("Teams flow (seeded)", () => {
cy.getAttached(".input-field").click().type("50");
}
);
- cy.getAttached(".schedule-editor-modal__btn-wrap").within(() => {
+ cy.getAttached(".modal-cta-wrap").within(() => {
cy.findByRole("button", { name: /schedule/i }).click();
});
});
diff --git a/frontend/components/AddHostsModal/DownloadInstallers/_styles.scss b/frontend/components/AddHostsModal/DownloadInstallers/_styles.scss
index 48aff6b09b..241b0347d7 100644
--- a/frontend/components/AddHostsModal/DownloadInstallers/_styles.scss
+++ b/frontend/components/AddHostsModal/DownloadInstallers/_styles.scss
@@ -71,11 +71,6 @@
}
}
- &__button--download {
- width: 154px;
- height: 38px;
- }
-
&__success {
display: flex;
flex-direction: column;
diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
index 63e2e8230f..cbfce304b0 100644
--- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
+++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx
@@ -456,7 +456,7 @@ const PlatformWrapper = ({
-
+
Done
diff --git a/frontend/components/AddHostsModal/_styles.scss b/frontend/components/AddHostsModal/_styles.scss
deleted file mode 100644
index 61f05cd9c5..0000000000
--- a/frontend/components/AddHostsModal/_styles.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.add-hosts-modal {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/components/DeleteSecretModal/DeleteSecretModal.tsx b/frontend/components/DeleteSecretModal/DeleteSecretModal.tsx
index bf9fb62f5e..8ade21920a 100644
--- a/frontend/components/DeleteSecretModal/DeleteSecretModal.tsx
+++ b/frontend/components/DeleteSecretModal/DeleteSecretModal.tsx
@@ -8,6 +8,7 @@ interface IDeleteSecretModal {
teams: ITeam[];
onDeleteSecret: () => void;
toggleDeleteSecretModal: () => void;
+ isUpdatingSecret: boolean;
}
const baseClass = "delete-secret-modal";
@@ -17,6 +18,7 @@ const DeleteSecretModal = ({
teams,
onDeleteSecret,
toggleDeleteSecretModal,
+ isUpdatingSecret,
}: IDeleteSecretModal): JSX.Element => {
const renderTeam = () => {
if (typeof selectedTeam === "string") {
@@ -50,20 +52,17 @@ const DeleteSecretModal = ({
- Cancel
-
-
Delete
+
+ Cancel
+
diff --git a/frontend/components/DeleteSecretModal/_styles.scss b/frontend/components/DeleteSecretModal/_styles.scss
index 13b86a3b8a..5a18c1622d 100644
--- a/frontend/components/DeleteSecretModal/_styles.scss
+++ b/frontend/components/DeleteSecretModal/_styles.scss
@@ -1,8 +1,4 @@
.delete-secret-modal {
- &__btn {
- margin-left: $pad-large;
- }
-
&__error {
color: $ui-error;
}
diff --git a/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx b/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx
index 95aeab1fde..ef738f8be2 100644
--- a/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx
+++ b/frontend/components/EnrollSecretModal/EnrollSecretModal.tsx
@@ -95,7 +95,7 @@ const EnrollSecretModal = ({
-
+
Done
diff --git a/frontend/components/EnrollSecretTable/EnrollSecretRow/_styles.scss b/frontend/components/EnrollSecretTable/EnrollSecretRow/_styles.scss
index 3495365c9b..bb992c5de5 100644
--- a/frontend/components/EnrollSecretTable/EnrollSecretRow/_styles.scss
+++ b/frontend/components/EnrollSecretTable/EnrollSecretRow/_styles.scss
@@ -35,7 +35,7 @@
align-items: center;
position: absolute;
right: 16px;
- top: 13px;
+ top: 16px;
height: 16px;
span {
@@ -81,11 +81,8 @@
}
}
- &__edit-secret-btn {
- margin: $pad-small $pad-small $pad-xsmall $pad-large;
- }
-
+ &__edit-secret-btn,
&__delete-secret-btn {
- margin: $pad-small 0 $pad-xsmall;
+ margin: $pad-xsmall $pad-small $pad-large;
}
}
diff --git a/frontend/components/Modal/_styles.scss b/frontend/components/Modal/_styles.scss
index c13e59b39b..f9b7d059da 100644
--- a/frontend/components/Modal/_styles.scss
+++ b/frontend/components/Modal/_styles.scss
@@ -23,12 +23,9 @@
.modal-cta-wrap {
display: flex;
- justify-content: flex-end;
+ flex-direction: row-reverse;
margin-top: $pad-large;
-
- :nth-child(2) {
- margin-left: $pad-medium;
- }
+ gap: $pad-medium;
}
}
diff --git a/frontend/components/SecretEditorModal/SecretEditorModal.tsx b/frontend/components/SecretEditorModal/SecretEditorModal.tsx
index efee863b0f..624730cd18 100644
--- a/frontend/components/SecretEditorModal/SecretEditorModal.tsx
+++ b/frontend/components/SecretEditorModal/SecretEditorModal.tsx
@@ -8,12 +8,13 @@ import Button from "components/buttons/Button";
// @ts-ignore
import InputField from "components/forms/fields/InputField";
-interface IAddSecretModal {
+interface ISecretEditorModalProps {
selectedTeam: number;
onSaveSecret: (newEnrollSecret: string) => void;
teams: ITeam[];
toggleSecretEditorModal: () => void;
selectedSecret: IEnrollSecret | undefined;
+ isUpdatingSecret: boolean;
}
const baseClass = "secret-editor-modal";
@@ -35,7 +36,8 @@ const SecretEditorModal = ({
teams,
toggleSecretEditorModal,
selectedSecret,
-}: IAddSecretModal): JSX.Element => {
+ isUpdatingSecret,
+}: ISecretEditorModalProps): JSX.Element => {
const [enrollSecretString, setEnrollSecretString] = useState(
selectedSecret ? selectedSecret.secret : randomSecretGenerator()
);
@@ -99,7 +101,12 @@ const SecretEditorModal = ({
/>
-
+
Save
diff --git a/frontend/components/SecretField/_styles.scss b/frontend/components/SecretField/_styles.scss
index 8c6166fa98..0293fbf25a 100644
--- a/frontend/components/SecretField/_styles.scss
+++ b/frontend/components/SecretField/_styles.scss
@@ -87,12 +87,4 @@
margin-bottom: $pad-medium;
}
}
-
- &__edit-secret-btn {
- margin: $pad-small $pad-small $pad-xsmall $pad-large;
- }
-
- &__delete-secret-btn {
- margin: $pad-small 0 $pad-xsmall;
- }
}
diff --git a/frontend/components/Spinner/Spinner.tsx b/frontend/components/Spinner/Spinner.tsx
index bcdf8bcd3c..1fd14e0f36 100644
--- a/frontend/components/Spinner/Spinner.tsx
+++ b/frontend/components/Spinner/Spinner.tsx
@@ -1,19 +1,41 @@
import React from "react";
+import classnames from "classnames";
interface ISpinnerProps {
small?: boolean;
+ button?: boolean;
+ white?: boolean;
}
-const baseClass = "loading-spinner";
-
-const Spinner = ({ small }: ISpinnerProps): JSX.Element => {
+const Spinner = ({ small, button, white }: ISpinnerProps): JSX.Element => {
+ const classOptions = classnames(`loading-spinner`, {
+ small,
+ button,
+ white,
+ });
return (
-
-
-
-
-
-
+
);
diff --git a/frontend/components/Spinner/_styles.scss b/frontend/components/Spinner/_styles.scss
index b056f7963a..0a582700a4 100644
--- a/frontend/components/Spinner/_styles.scss
+++ b/frontend/components/Spinner/_styles.scss
@@ -9,79 +9,86 @@
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.1);
border-radius: 8px;
- &__ring {
- box-sizing: border-box;
- display: inline-block;
+ .loader {
position: relative;
- width: 32px;
- height: 32px;
- border-radius: 100%;
- border: 4px solid $core-vibrant-blue;
-
- div {
- box-sizing: border-box;
+ margin: 0 auto;
+ width: 34px;
+
+ &:before {
+ content: "";
display: block;
- position: absolute;
- width: 32px;
- height: 32px;
- margin: -4px;
- border: 4px solid;
- border-radius: 50%;
- animation: ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
- border-color: $ui-vibrant-blue-25 transparent transparent transparent;
+ padding-top: 100%;
}
}
-
- div:nth-child(1) {
- animation-delay: -0.45s;
- }
- div:nth-child(2) {
- animation-delay: -0.3s;
- }
-
- div:nth-child(3) {
- animation-delay: -0.15s;
- }
-}
-
-.loading-spinner.small {
- background: none;
- box-shadow: none;
- height: auto;
- margin: 0;
- transform: scale(0.7);
-}
-
-button {
- position: relative;
- .loading-spinner {
+ &.button {
background: none;
- box-shadow: none;
- margin: 0;
- transform: scale(0.7);
position: absolute;
- &__ring {
- border-color: $core-white;
-
- div {
- border-color: $core-vibrant-blue transparent transparent transparent;
- width: 32px;
- height: 32px;
+ width: auto;
+ height: auto;
+ top: auto;
+ box-shadow: none;
+ z-index: 9;
+ margin: 0;
+ padding: 0;
+ &.white {
+ .path {
+ stroke: $core-white;
}
}
+ .loader {
+ width: 23px;
+ }
+ .background {
+ display: none;
+ }
}
-}
-.spinner-wrap {
- display: flex;
-}
+ .circular {
+ animation: rotate 2s linear infinite;
+ height: 100%;
+ transform-origin: center center;
+ width: 100%;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: auto;
+ }
-@keyframes ring {
- 0% {
- transform: rotate(0deg);
+ .path {
+ stroke-dasharray: 1, 200;
+ stroke-dashoffset: 0;
+ stroke: $core-vibrant-blue;
+ animation: dash 1.5s ease-in-out infinite;
+ stroke-linecap: round;
}
- 100% {
- transform: rotate(360deg);
+
+ .background {
+ stroke: $ui-vibrant-blue-25;
}
-}
+
+ @keyframes rotate {
+ 100% {
+ transform: rotate(360deg);
+ }
+ }
+
+ @keyframes dash {
+ 0% {
+ stroke-dasharray: 1, 200;
+ stroke-dashoffset: 0;
+ }
+
+ 50% {
+ stroke-dasharray: 89, 200;
+ stroke-dashoffset: -35px;
+ }
+
+ 100% {
+ stroke-dasharray: 89, 200;
+ stroke-dashoffset: -124px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/components/buttons/Button/Button.tsx b/frontend/components/buttons/Button/Button.tsx
index 24c6586cfd..e3bb5996cb 100644
--- a/frontend/components/buttons/Button/Button.tsx
+++ b/frontend/components/buttons/Button/Button.tsx
@@ -1,5 +1,6 @@
import React from "react";
import classnames from "classnames";
+import Spinner from "components/Spinner";
const baseClass = "button";
@@ -35,6 +36,7 @@ export interface IButtonProps {
onClick?:
| ((value?: any) => void)
| ((evt: React.MouseEvent
) => void);
+ isLoading?: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -95,6 +97,7 @@ class Button extends React.Component {
type,
title,
variant,
+ isLoading,
} = this.props;
const fullClassName = classnames(
baseClass,
@@ -105,6 +108,11 @@ class Button extends React.Component {
[`${baseClass}--${size}`]: size !== undefined,
}
);
+ const onWhite =
+ variant === "text-link" ||
+ variant === "inverse" ||
+ variant === "text-icon" ||
+ variant === "label";
return (
{
title={title}
ref={setRef}
>
- {children}
+
+ {children}
+
+ {isLoading && }
);
}
diff --git a/frontend/components/buttons/Button/_styles.scss b/frontend/components/buttons/Button/_styles.scss
index 85f2bf84bc..c4ba344a9d 100644
--- a/frontend/components/buttons/Button/_styles.scss
+++ b/frontend/components/buttons/Button/_styles.scss
@@ -45,6 +45,7 @@ $base-class: "button";
height: 38px;
top: 0;
border: 0;
+ position: relative;
cursor: pointer;
&:focus {
@@ -57,6 +58,7 @@ $base-class: "button";
$core-vibrant-blue-over,
$core-vibrant-blue-down
);
+ display: flex;
}
&--success {
@@ -69,6 +71,15 @@ $base-class: "button";
$core-vibrant-red-over,
$core-vibrant-red-down
);
+ display: flex;
+
+ .loading-spinner {
+ &__ring {
+ div {
+ border-color: $ui-error transparent transparent transparent;
+ }
+ }
+ }
}
&--blue-green {
@@ -320,4 +331,14 @@ $base-class: "button";
display: flex;
justify-content: space-between;
}
+
+ .transparent-text {
+ opacity: 0;
+ }
+
+ .children-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
}
diff --git a/frontend/components/forms/ChangeEmailForm/ChangeEmailForm.jsx b/frontend/components/forms/ChangeEmailForm/ChangeEmailForm.jsx
index e3cb33469b..da840134dd 100644
--- a/frontend/components/forms/ChangeEmailForm/ChangeEmailForm.jsx
+++ b/frontend/components/forms/ChangeEmailForm/ChangeEmailForm.jsx
@@ -28,15 +28,11 @@ class ChangeEmailForm extends Component {
label="Password"
type="password"
/>
-
-
+
+
Submit
-
+
Cancel
diff --git a/frontend/components/forms/ChangeEmailForm/_styles.scss b/frontend/components/forms/ChangeEmailForm/_styles.scss
deleted file mode 100644
index f61dfe8e96..0000000000
--- a/frontend/components/forms/ChangeEmailForm/_styles.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-.change-email-form {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx
index 180ccdeafa..68082bb40b 100644
--- a/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx
+++ b/frontend/components/forms/ChangePasswordForm/ChangePasswordForm.jsx
@@ -49,15 +49,11 @@ class ChangePasswordForm extends Component {
label="New password confirmation"
type="password"
/>
-
-
+
+
Change password
-
+
Cancel
diff --git a/frontend/components/forms/ChangePasswordForm/_styles.scss b/frontend/components/forms/ChangePasswordForm/_styles.scss
deleted file mode 100644
index 92a2a90f99..0000000000
--- a/frontend/components/forms/ChangePasswordForm/_styles.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.change-password-form {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- &:last-child {
- margin-right: $pad-medium;
- }
- }
-}
diff --git a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx
index 72417f2240..253ccf9d74 100644
--- a/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx
+++ b/frontend/components/forms/ConfigurePackQueryForm/ConfigurePackQueryForm.jsx
@@ -144,13 +144,9 @@ export class ConfigurePackQueryForm extends Component {
label="Shard"
type="number"
/>
-
+
{renderCancelButton()}
-
+
Save
diff --git a/frontend/components/forms/ConfigurePackQueryForm/_styles.scss b/frontend/components/forms/ConfigurePackQueryForm/_styles.scss
index 4ef798750f..48500906c5 100644
--- a/frontend/components/forms/ConfigurePackQueryForm/_styles.scss
+++ b/frontend/components/forms/ConfigurePackQueryForm/_styles.scss
@@ -33,14 +33,4 @@
margin-left: 6px;
}
}
-
- &__btn-wrapper {
- padding-top: 20px;
-
- .button {
- display: block;
- width: 100%;
- margin-bottom: 20px;
- }
- }
}
diff --git a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx
index 24bc734b04..653fb10ef9 100644
--- a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx
+++ b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.jsx
@@ -52,11 +52,7 @@ class ConfirmInviteForm extends Component {
/>
-
+
Submit
diff --git a/frontend/components/forms/ForgotPasswordForm/ForgotPasswordForm.jsx b/frontend/components/forms/ForgotPasswordForm/ForgotPasswordForm.jsx
index 958629b74c..863bd5fdf8 100644
--- a/frontend/components/forms/ForgotPasswordForm/ForgotPasswordForm.jsx
+++ b/frontend/components/forms/ForgotPasswordForm/ForgotPasswordForm.jsx
@@ -29,7 +29,8 @@ class ForgotPasswordForm extends Component {
Send email
diff --git a/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.jsx b/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.jsx
index 382040a35f..92dcc42f3a 100644
--- a/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.jsx
+++ b/frontend/components/forms/RegistrationForm/AdminDetails/AdminDetails.jsx
@@ -81,7 +81,7 @@ class AdminDetails extends Component {
type="submit"
tabIndex={tabIndex}
disabled={!currentPage}
- className="button button--brand"
+ variant="brand"
>
Next
diff --git a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tsx b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tsx
index 34553a314c..a9a91e9239 100644
--- a/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tsx
+++ b/frontend/components/forms/RegistrationForm/ConfirmationPage/ConfirmationPage.tsx
@@ -106,7 +106,7 @@ const ConfirmationPage = ({
type="submit"
tabIndex={tabIndex}
disabled={!currentPage}
- className="button button--brand"
+ variant="brand"
>
Confirm
diff --git a/frontend/components/forms/RegistrationForm/FleetDetails/FleetDetails.jsx b/frontend/components/forms/RegistrationForm/FleetDetails/FleetDetails.jsx
index 8bd35d2472..208bb16064 100644
--- a/frontend/components/forms/RegistrationForm/FleetDetails/FleetDetails.jsx
+++ b/frontend/components/forms/RegistrationForm/FleetDetails/FleetDetails.jsx
@@ -59,7 +59,7 @@ class FleetDetails extends Component {
type="submit"
tabIndex={tabIndex}
disabled={!currentPage}
- className="button button--brand"
+ variant="brand"
>
Next
diff --git a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx
index 7d783e9e0e..dbeee1e11e 100644
--- a/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx
+++ b/frontend/components/forms/RegistrationForm/OrgDetails/OrgDetails.jsx
@@ -61,7 +61,7 @@ class OrgDetails extends Component {
type="submit"
tabIndex={tabIndex}
disabled={!currentPage}
- className="button button--brand"
+ variant="brand"
>
Next
diff --git a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx
index e4a86c34c8..a34e7f912b 100644
--- a/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx
+++ b/frontend/components/forms/ResetPasswordForm/ResetPasswordForm.jsx
@@ -42,8 +42,9 @@ class ResetPasswordForm extends Component {
/>
Reset password
diff --git a/frontend/components/forms/admin/EditUserForm/_styles.scss b/frontend/components/forms/admin/EditUserForm/_styles.scss
deleted file mode 100644
index d813777236..0000000000
--- a/frontend/components/forms/admin/EditUserForm/_styles.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-.edit-user-form {
- box-sizing: border-box;
- height: 100%;
- position: relative;
- margin-top: $pad-large;
-
- &__btn-wrap {
- display: flex;
- justify-content: flex-end;
- }
-
- .form-field {
- margin-bottom: $pad-large;
- }
-
- form &__input {
- font-size: $small;
- width: 100%;
- padding: $pad-xsmall $pad-small;
- }
-
- form &__label {
- font-size: $small;
- margin-bottom: 0;
- font-weight: $bold;
- }
-
- &__form-btn {
- &--cancel {
- margin-right: $pad-medium;
- }
- }
-}
diff --git a/frontend/components/forms/packs/EditPackForm/EditPackForm.tsx b/frontend/components/forms/packs/EditPackForm/EditPackForm.tsx
index f25f2e181f..9b26e9629f 100644
--- a/frontend/components/forms/packs/EditPackForm/EditPackForm.tsx
+++ b/frontend/components/forms/packs/EditPackForm/EditPackForm.tsx
@@ -30,6 +30,7 @@ interface IEditPackForm {
formData: IEditPackFormData;
scheduledQueries: IScheduledQuery[];
isLoadingPackQueries: boolean;
+ isUpdatingPack: boolean;
}
interface IEditPackFormData {
@@ -51,6 +52,7 @@ const EditPackForm = ({
targetsCount,
isPremiumTier,
formData,
+ isUpdatingPack,
}: IEditPackForm): JSX.Element => {
const [errors, setErrors] = useState<{ [key: string]: string }>({});
const [packName, setPackName] = useState(formData.name);
@@ -139,7 +141,12 @@ const EditPackForm = ({
Cancel
-
+
Save
diff --git a/frontend/components/forms/packs/EditPackForm/_styles.scss b/frontend/components/forms/packs/EditPackForm/_styles.scss
index 571e28af19..72de07dd60 100644
--- a/frontend/components/forms/packs/EditPackForm/_styles.scss
+++ b/frontend/components/forms/packs/EditPackForm/_styles.scss
@@ -24,11 +24,9 @@
}
&__pack-buttons {
- margin: $pad-large 0;
- text-align: right;
-
- .button {
- margin-left: 15px;
- }
+ display: flex;
+ justify-content: flex-end;
+ margin-top: $pad-large;
+ gap: $pad-medium;
}
}
diff --git a/frontend/components/forms/packs/PackForm/PackForm.tsx b/frontend/components/forms/packs/PackForm/PackForm.tsx
index 5c41c21a5e..20fc8bf26a 100644
--- a/frontend/components/forms/packs/PackForm/PackForm.tsx
+++ b/frontend/components/forms/packs/PackForm/PackForm.tsx
@@ -25,6 +25,7 @@ interface IPackForm {
) => boolean;
selectedTargetsCount?: number;
isPremiumTier?: boolean;
+ isUpdatingPack: boolean;
}
const EditPackForm = ({
@@ -33,6 +34,7 @@ const EditPackForm = ({
onFetchTargets,
selectedTargetsCount,
isPremiumTier,
+ isUpdatingPack,
}: IPackForm): JSX.Element => {
const [errors, setErrors] = useState<{ [key: string]: string }>({});
const [packName, setPackName] = useState
("");
@@ -111,7 +113,11 @@ const EditPackForm = ({
/>
-
+
Save query pack
diff --git a/frontend/pages/Homepage/cards/WelcomeHost/WelcomeHost.tsx b/frontend/pages/Homepage/cards/WelcomeHost/WelcomeHost.tsx
index 292c109fa1..67f74600b0 100644
--- a/frontend/pages/Homepage/cards/WelcomeHost/WelcomeHost.tsx
+++ b/frontend/pages/Homepage/cards/WelcomeHost/WelcomeHost.tsx
@@ -155,7 +155,8 @@ const WelcomeHost = ({
Add hosts
diff --git a/frontend/pages/UserSettingsPage/UserSettingsPage.tsx b/frontend/pages/UserSettingsPage/UserSettingsPage.tsx
index 5e2b591695..8287516d1e 100644
--- a/frontend/pages/UserSettingsPage/UserSettingsPage.tsx
+++ b/frontend/pages/UserSettingsPage/UserSettingsPage.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useContext, useEffect } from "react";
+import React, { useState, useContext } from "react";
import { InjectedRouter } from "react-router";
import { AppContext } from "context/app";
@@ -222,11 +222,8 @@ const UserSettingsPage = ({
src={ExternalURLIcon}
/>
-
-
+
+
Done
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Advanced/Advanced.tsx b/frontend/pages/admin/AppSettingsPage/cards/Advanced/Advanced.tsx
index ba83e32fce..61da4d5b8d 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Advanced/Advanced.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Advanced/Advanced.tsx
@@ -16,6 +16,7 @@ const baseClass = "app-config-form";
const Advanced = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState({
domain: appConfig.smtp_settings.domain || "",
@@ -179,6 +180,8 @@ const Advanced = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Agents/Agents.tsx b/frontend/pages/admin/AppSettingsPage/cards/Agents/Agents.tsx
index c486f341e3..d492c5ee8c 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Agents/Agents.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Agents/Agents.tsx
@@ -21,6 +21,7 @@ const Agents = ({
appConfig,
handleSubmit,
isPremiumTier,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const { ADMIN_TEAMS } = paths;
@@ -122,6 +123,8 @@ const Agents = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/FleetDesktop/FleetDesktop.tsx b/frontend/pages/admin/AppSettingsPage/cards/FleetDesktop/FleetDesktop.tsx
index 5737009bc7..d02f923c45 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/FleetDesktop/FleetDesktop.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/FleetDesktop/FleetDesktop.tsx
@@ -22,6 +22,7 @@ const FleetDesktop = ({
appConfig,
handleSubmit,
isPremiumTier,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState<
Pick
@@ -102,6 +103,8 @@ const FleetDesktop = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx b/frontend/pages/admin/AppSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
index 98ac423818..978f2dda2b 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/HostStatusWebhook/HostStatusWebhook.tsx
@@ -22,6 +22,7 @@ const baseClass = "app-config-form";
const HostStatusWebhook = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [
showHostStatusWebhookPreviewModal,
@@ -206,6 +207,8 @@ const HostStatusWebhook = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Info/Info.tsx b/frontend/pages/admin/AppSettingsPage/cards/Info/Info.tsx
index 2a6de707d9..d7be5ad36f 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Info/Info.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Info/Info.tsx
@@ -18,6 +18,7 @@ const baseClass = "app-config-form";
const Info = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState({
orgName: appConfig.org_info.org_name || "",
@@ -92,6 +93,8 @@ const Info = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Smtp/Smtp.tsx b/frontend/pages/admin/AppSettingsPage/cards/Smtp/Smtp.tsx
index 8d9ca13c37..7d350a0cc6 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Smtp/Smtp.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Smtp/Smtp.tsx
@@ -22,6 +22,7 @@ const baseClass = "app-config-form";
const Smtp = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState({
enableSMTP: appConfig.smtp_settings.enable_smtp || false,
@@ -252,6 +253,8 @@ const Smtp = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Sso/Sso.tsx b/frontend/pages/admin/AppSettingsPage/cards/Sso/Sso.tsx
index 2184bf790f..8c2926c5d0 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Sso/Sso.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Sso/Sso.tsx
@@ -20,6 +20,7 @@ const Sso = ({
appConfig,
handleSubmit,
isPremiumTier,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState({
enableSSO: appConfig.sso_settings.enable_sso || false,
@@ -242,6 +243,8 @@ const Sso = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/Statistics/Statistics.tsx b/frontend/pages/admin/AppSettingsPage/cards/Statistics/Statistics.tsx
index 1c279df129..3da9ad716b 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/Statistics/Statistics.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/Statistics/Statistics.tsx
@@ -17,6 +17,7 @@ const baseClass = "app-config-form";
const Statistics = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [
showUsageStatsPreviewModal,
@@ -125,7 +126,12 @@ const Statistics = ({
-
+
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/WebAddress/WebAddress.tsx b/frontend/pages/admin/AppSettingsPage/cards/WebAddress/WebAddress.tsx
index c5ab794830..d5d2eaf38c 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/WebAddress/WebAddress.tsx
+++ b/frontend/pages/admin/AppSettingsPage/cards/WebAddress/WebAddress.tsx
@@ -14,6 +14,7 @@ const baseClass = "app-config-form";
const WebAddress = ({
appConfig,
handleSubmit,
+ isUpdatingSettings,
}: IAppConfigFormProps): JSX.Element => {
const [formData, setFormData] = useState({
serverURL: appConfig.server_settings.server_url || "",
@@ -78,6 +79,8 @@ const WebAddress = ({
type="submit"
variant="brand"
disabled={Object.keys(formErrors).length > 0}
+ className="save-loading"
+ isLoading={isUpdatingSettings}
>
Save
diff --git a/frontend/pages/admin/AppSettingsPage/cards/constants.ts b/frontend/pages/admin/AppSettingsPage/cards/constants.ts
index 37dc1bbc55..7be2f5f7b1 100644
--- a/frontend/pages/admin/AppSettingsPage/cards/constants.ts
+++ b/frontend/pages/admin/AppSettingsPage/cards/constants.ts
@@ -5,6 +5,7 @@ export const DEFAULT_TRANSPARENCY_URL = "https://fleetdm.com/transparency";
export interface IAppConfigFormProps {
appConfig: IConfig;
isPremiumTier?: boolean;
+ isUpdatingSettings?: boolean;
handleSubmit: any;
}
diff --git a/frontend/pages/admin/AppSettingsPage/components/OrgSettingsForm/OrgSettingsForm.tsx b/frontend/pages/admin/AppSettingsPage/components/OrgSettingsForm/OrgSettingsForm.tsx
index 0117fa79b8..cd4e4ee916 100644
--- a/frontend/pages/admin/AppSettingsPage/components/OrgSettingsForm/OrgSettingsForm.tsx
+++ b/frontend/pages/admin/AppSettingsPage/components/OrgSettingsForm/OrgSettingsForm.tsx
@@ -36,6 +36,7 @@ const OrgSettingsForm = ({
const handlePageError = useErrorHandler();
const [activeSection, setActiveSection] = useState("info");
+ const [isUpdatingSettings, setIsUpdatingSettings] = useState(false);
const { data: appConfig, isLoading, refetch: refetchConfig } = useQuery<
IConfig,
@@ -58,6 +59,8 @@ const OrgSettingsForm = ({
return false;
}
+ setIsUpdatingSettings(true);
+
const diff = deepDifference(formData, appConfig);
// send all formData.agent_options because diff overrides all agent options
diff.agent_options = formData.agent_options;
@@ -84,6 +87,7 @@ const OrgSettingsForm = ({
})
.finally(() => {
refetchConfig();
+ setIsUpdatingSettings(false);
});
},
[appConfig]
@@ -103,36 +107,55 @@ const OrgSettingsForm = ({
return (
<>
{activeSection === "info" && (
-
+
)}
{activeSection === "webaddress" && (
-
+
)}
{activeSection === "sso" && (
)}
{activeSection === "smtp" && (
-
+
)}
{activeSection === "agents" && (
)}
{activeSection === "host-status-webhook" && (
)}
{activeSection === "statistics" && (
-
+
)}
{activeSection === "advanced" && (
@@ -142,6 +165,7 @@ const OrgSettingsForm = ({
appConfig={appConfig}
isPremiumTier={isPremiumTier}
handleSubmit={onFormSubmit}
+ isUpdatingSettings={isUpdatingSettings}
/>
)}
>
diff --git a/frontend/pages/admin/IntegrationsPage/IntegrationsPage.tsx b/frontend/pages/admin/IntegrationsPage/IntegrationsPage.tsx
index b3a9d28d35..3dfceafdfc 100644
--- a/frontend/pages/admin/IntegrationsPage/IntegrationsPage.tsx
+++ b/frontend/pages/admin/IntegrationsPage/IntegrationsPage.tsx
@@ -54,6 +54,9 @@ const IntegrationsPage = (): JSX.Element => {
integrationEditing,
setIntegrationEditing,
] = useState();
+ const [isUpdatingIntegration, setIsUpdatingIntegration] = useState(
+ false
+ );
const [jiraIntegrations, setJiraIntegrations] = useState<
IJiraIntegration[]
>();
@@ -233,7 +236,7 @@ const IntegrationsPage = (): JSX.Element => {
},
});
};
-
+ setIsUpdatingIntegration(true);
deleteIntegrationDestination()
.then(() => {
renderFlash(
@@ -264,6 +267,7 @@ const IntegrationsPage = (): JSX.Element => {
);
})
.finally(() => {
+ setIsUpdatingIntegration(false);
toggleDeleteIntegrationModal();
});
}
@@ -442,6 +446,7 @@ const IntegrationsPage = (): JSX.Element => {
integrationEditing?.groupId?.toString() ||
""
}
+ isUpdatingIntegration={isUpdatingIntegration}
/>
)}
{showEditIntegrationModal && integrations && (
diff --git a/frontend/pages/admin/IntegrationsPage/components/CreateIntegrationModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/components/CreateIntegrationModal/_styles.scss
index 91e8836b30..e43dbdebe8 100644
--- a/frontend/pages/admin/IntegrationsPage/components/CreateIntegrationModal/_styles.scss
+++ b/frontend/pages/admin/IntegrationsPage/components/CreateIntegrationModal/_styles.scss
@@ -15,14 +15,4 @@
&__info-header {
margin-bottom: $pad-xlarge;
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
}
diff --git a/frontend/pages/admin/IntegrationsPage/components/DeleteIntegrationModal/DeleteIntegrationModal.tsx b/frontend/pages/admin/IntegrationsPage/components/DeleteIntegrationModal/DeleteIntegrationModal.tsx
index 5d38fca8b9..9d230e8138 100644
--- a/frontend/pages/admin/IntegrationsPage/components/DeleteIntegrationModal/DeleteIntegrationModal.tsx
+++ b/frontend/pages/admin/IntegrationsPage/components/DeleteIntegrationModal/DeleteIntegrationModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
@@ -10,6 +10,7 @@ interface IDeleteIntegrationModalProps {
projectKey: string;
onSubmit: () => void;
onCancel: () => void;
+ isUpdatingIntegration: boolean;
}
const DeleteIntegrationModal = ({
@@ -17,6 +18,7 @@ const DeleteIntegrationModal = ({
projectKey,
onSubmit,
onCancel,
+ isUpdatingIntegration,
}: IDeleteIntegrationModalProps): JSX.Element => {
return (
The automations that use this integration will be turned off.
+
+ Delete
+
Cancel
-
- Delete
-
diff --git a/frontend/pages/admin/IntegrationsPage/components/EditIntegrationModal/_styles.scss b/frontend/pages/admin/IntegrationsPage/components/EditIntegrationModal/_styles.scss
deleted file mode 100644
index 60adcf5f04..0000000000
--- a/frontend/pages/admin/IntegrationsPage/components/EditIntegrationModal/_styles.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.edit-team-modal {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/IntegrationForm.tsx b/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/IntegrationForm.tsx
index 8be3992555..de9e41ba61 100644
--- a/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/IntegrationForm.tsx
+++ b/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/IntegrationForm.tsx
@@ -239,7 +239,7 @@ const IntegrationForm = ({
}
/>
)}
-
+
the integration.
>
-
+
Cancel
diff --git a/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/_styles.scss b/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/_styles.scss
index 42cb58b9a4..01de0f74b7 100644
--- a/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/_styles.scss
+++ b/frontend/pages/admin/IntegrationsPage/components/IntegrationForm/_styles.scss
@@ -84,15 +84,6 @@
}
}
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
-
&__invite-admin {
margin-bottom: 8px;
}
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPage.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPage.tsx
index 149100d656..32e931c97c 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPage.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/MembersPage.tsx
@@ -67,6 +67,7 @@ const MembersPage = ({
false
);
const [isLoading, setIsLoading] = useState(false);
+ const [isUpdatingMembers, setIsUpdatingMembers] = useState(false);
const [userEditing, setUserEditing] = useState();
const [searchString, setSearchString] = useState("");
const [createUserErrors, setCreateUserErrors] = useState(
@@ -143,7 +144,7 @@ const MembersPage = ({
const onRemoveMemberSubmit = useCallback(() => {
const removedUsers = { users: [{ id: userEditing?.id }] };
- setIsLoading(true);
+ setIsUpdatingMembers(true);
teamsAPI
.removeMembers(teamId, removedUsers)
.then(() => {
@@ -160,7 +161,7 @@ const MembersPage = ({
renderFlash("error", "Unable to remove members. Please try again.")
)
.finally(() => {
- setIsLoading(false);
+ setIsUpdatingMembers(false);
toggleRemoveMemberModal();
refetchUsers();
});
@@ -197,7 +198,7 @@ const MembersPage = ({
);
const onCreateMemberSubmit = (formData: IFormData) => {
- setIsLoading(true);
+ setIsUpdatingMembers(true);
if (formData.newUserType === NewUserType.AdminInvited) {
const requestData = {
@@ -238,7 +239,7 @@ const MembersPage = ({
}
})
.finally(() => {
- setIsLoading(false);
+ setIsUpdatingMembers(false);
});
} else {
const requestData = {
@@ -269,7 +270,7 @@ const MembersPage = ({
}
})
.finally(() => {
- setIsLoading(false);
+ setIsUpdatingMembers(false);
});
}
};
@@ -281,7 +282,7 @@ const MembersPage = ({
formData
);
- setIsLoading(true);
+ setIsUpdatingMembers(true);
const userName = userEditing?.name;
@@ -310,7 +311,6 @@ const MembersPage = ({
} else {
refetchUsers();
}
- setIsLoading(false);
toggleEditMemberModal();
})
.catch((userErrors: { data: IApiError }) => {
@@ -324,6 +324,9 @@ const MembersPage = ({
`Could not edit ${userName || "member"}. Please try again.`
);
}
+ })
+ .finally(() => {
+ setIsUpdatingMembers(false);
});
},
[toggleEditMemberModal, userEditing, refetchUsers]
@@ -451,7 +454,7 @@ const MembersPage = ({
isSsoEnabled={userEditing?.sso_enabled}
isModifiedByGlobalAdmin={isGlobalAdmin}
currentTeam={currentTeam}
- isLoading={isLoading}
+ isUpdatingUsers={isUpdatingMembers}
/>
)}
{showCreateUserModal && (
@@ -468,14 +471,14 @@ const MembersPage = ({
canUseSso={canUseSso}
currentTeam={currentTeam}
isModifiedByGlobalAdmin={isGlobalAdmin}
- isLoading={isLoading}
+ isUpdatingUsers={isUpdatingMembers}
/>
)}
{showRemoveMemberModal && currentTeam && (
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx
index 73e30002db..0a5fb0e1a4 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/AddMemberModal.tsx
@@ -65,21 +65,16 @@ const AddMemberModal = ({
>
-
+
Add member
-
+
Cancel
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/_styles.scss b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/_styles.scss
index fe39582dac..08174655e3 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/_styles.scss
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/AddMemberModal/_styles.scss
@@ -3,14 +3,4 @@
font-weight: $bold;
margin-bottom: 4px;
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
}
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/RemoveMemberModal.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/RemoveMemberModal.tsx
index 37aac54683..564d9384b4 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/RemoveMemberModal.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/RemoveMemberModal.tsx
@@ -1,15 +1,14 @@
-import React, { useEffect } from "react";
+import React from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
-import Spinner from "components/Spinner";
const baseClass = "remove-member-modal";
interface IDeleteTeamModalProps {
memberName: string;
teamName: string;
- isLoading: boolean;
+ isUpdatingMembers: boolean;
onSubmit: () => void;
onCancel: () => void;
}
@@ -17,7 +16,7 @@ interface IDeleteTeamModalProps {
const RemoveMemberModal = ({
memberName,
teamName,
- isLoading,
+ isUpdatingMembers,
onSubmit,
onCancel,
}: IDeleteTeamModalProps): JSX.Element => {
@@ -28,38 +27,31 @@ const RemoveMemberModal = ({
onEnter={onSubmit}
className={baseClass}
>
- {isLoading ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/_styles.scss b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/_styles.scss
index 411cd785fb..6541dc2212 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/_styles.scss
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPage/components/RemoveMemberModal/_styles.scss
@@ -7,14 +7,4 @@
&__warning {
color: $ui-error;
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
}
diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
index 92b4367a63..d981de7e76 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/TeamDetailsWrapper.tsx
@@ -127,9 +127,8 @@ const TeamDetailsWrapper = ({
const [backendValidators, setBackendValidators] = useState<{
[key: string]: string;
}>({});
- const [teamIsRemoving, setTeamIsRemoving] = useState
(false);
- const [teamIsEditing, setTeamIsEditing] = useState(false);
-
+ const [isUpdatingTeams, setIsUpdatingTeams] = useState(false);
+ const [isUpdatingSecret, setIsUpdatingSecret] = useState(false);
const { refetch: refetchMe } = useQuery(["me"], () => usersAPI.me(), {
enabled: false,
onSuccess: ({ user, available_teams }: IGetMeResponse) => {
@@ -235,7 +234,7 @@ const TeamDetailsWrapper = ({
if (enrollSecretString) {
newSecrets.push({ secret: enrollSecretString });
}
-
+ setIsUpdatingSecret(true);
try {
await enrollSecretsAPI.modifyTeamEnrollSecrets(teamIdFromURL, newSecrets);
refetchTeamSecrets();
@@ -254,6 +253,8 @@ const TeamDetailsWrapper = ({
selectedSecret ? "edit" : "add"
} enroll secret. Please try again.`
);
+ } finally {
+ setIsUpdatingSecret(false);
}
};
@@ -264,7 +265,7 @@ const TeamDetailsWrapper = ({
const newSecrets = currentSecrets.filter(
(s) => s.secret !== selectedSecret?.secret
);
-
+ setIsUpdatingSecret(true);
try {
await enrollSecretsAPI.modifyTeamEnrollSecrets(teamIdFromURL, newSecrets);
refetchTeamSecrets();
@@ -274,6 +275,8 @@ const TeamDetailsWrapper = ({
} catch (error) {
console.error(error);
renderFlash("error", "Could not delete enroll secret. Please try again.");
+ } finally {
+ setIsUpdatingSecret(false);
}
};
@@ -282,7 +285,7 @@ const TeamDetailsWrapper = ({
return false;
}
- setTeamIsRemoving(true);
+ setIsUpdatingTeams(true);
try {
await teamsAPI.destroy(currentTeam.id);
@@ -294,7 +297,7 @@ const TeamDetailsWrapper = ({
return false;
} finally {
toggleDeleteTeamModal();
- setTeamIsRemoving(false);
+ setIsUpdatingTeams(false);
}
}, [toggleDeleteTeamModal, currentTeam?.id]);
@@ -313,7 +316,7 @@ const TeamDetailsWrapper = ({
return;
}
- setTeamIsEditing(true);
+ setIsUpdatingTeams(true);
try {
await teamsAPI.update(updatedAttrs, currentTeam.id);
@@ -342,7 +345,7 @@ const TeamDetailsWrapper = ({
return false;
} finally {
- setTeamIsEditing(false);
+ setIsUpdatingTeams(false);
}
},
[toggleEditTeamModal, currentTeam, setBackendValidators]
@@ -503,6 +506,7 @@ const TeamDetailsWrapper = ({
onSaveSecret={onSaveSecret}
toggleSecretEditorModal={toggleSecretEditorModal}
selectedSecret={selectedSecret}
+ isUpdatingSecret={isUpdatingSecret}
/>
)}
{showDeleteSecretModal && (
@@ -511,6 +515,7 @@ const TeamDetailsWrapper = ({
selectedTeam={teamIdFromURL}
teams={teams || []}
toggleDeleteSecretModal={toggleDeleteSecretModal}
+ isUpdatingSecret={isUpdatingSecret}
/>
)}
{showDeleteTeamModal && (
@@ -518,7 +523,7 @@ const TeamDetailsWrapper = ({
onCancel={toggleDeleteTeamModal}
onSubmit={onDeleteSubmit}
name={currentTeam.name}
- isLoading={teamIsRemoving}
+ isUpdatingTeams={isUpdatingTeams}
/>
)}
{showEditTeamModal && (
@@ -527,7 +532,7 @@ const TeamDetailsWrapper = ({
onSubmit={onEditSubmit}
defaultName={currentTeam.name}
backendValidators={backendValidators}
- isLoading={teamIsEditing}
+ isUpdatingTeams={isUpdatingTeams}
/>
)}
{children}
diff --git a/frontend/pages/admin/TeamManagementPage/TeamManagementPage.tsx b/frontend/pages/admin/TeamManagementPage/TeamManagementPage.tsx
index 06e325ded1..237b4cd126 100644
--- a/frontend/pages/admin/TeamManagementPage/TeamManagementPage.tsx
+++ b/frontend/pages/admin/TeamManagementPage/TeamManagementPage.tsx
@@ -25,9 +25,7 @@ const noTeamsClass = "no-teams";
const TeamManagementPage = (): JSX.Element => {
const { renderFlash } = useContext(NotificationContext);
- const [teamIsLoading, setTeamIsLoading] = useState(false);
- const [teamIsEditing, setTeamIsEditing] = useState(false);
- const [teamIsRemoving, setTeamIsRemoving] = useState(false);
+ const [isUpdatingTeams, setIsUpdatingTeams] = useState(false);
const [showCreateTeamModal, setShowCreateTeamModal] = useState(false);
const [showDeleteTeamModal, setShowDeleteTeamModal] = useState(false);
const [showEditTeamModal, setShowEditTeamModal] = useState(false);
@@ -96,7 +94,7 @@ const TeamManagementPage = (): JSX.Element => {
const onCreateSubmit = useCallback(
(formData: ITeamFormData) => {
- setTeamIsLoading(true);
+ setIsUpdatingTeams(true);
teamsAPI
.create(formData)
.then(() => {
@@ -116,7 +114,7 @@ const TeamManagementPage = (): JSX.Element => {
}
})
.finally(() => {
- setTeamIsLoading(false);
+ setIsUpdatingTeams(false);
});
},
[toggleCreateTeamModal]
@@ -124,7 +122,7 @@ const TeamManagementPage = (): JSX.Element => {
const onDeleteSubmit = useCallback(() => {
if (teamEditing) {
- setTeamIsRemoving(true);
+ setIsUpdatingTeams(true);
teamsAPI
.destroy(teamEditing.id)
.then(() => {
@@ -137,7 +135,7 @@ const TeamManagementPage = (): JSX.Element => {
);
})
.finally(() => {
- setTeamIsRemoving(true);
+ setIsUpdatingTeams(false);
refetchTeams();
toggleDeleteTeamModal();
});
@@ -149,7 +147,7 @@ const TeamManagementPage = (): JSX.Element => {
if (formData.name === teamEditing?.name) {
toggleEditTeamModal();
} else if (teamEditing) {
- setTeamIsEditing(true);
+ setIsUpdatingTeams(true);
teamsAPI
.update(formData, teamEditing.id)
.then(() => {
@@ -175,7 +173,7 @@ const TeamManagementPage = (): JSX.Element => {
}
})
.finally(() => {
- setTeamIsEditing(false);
+ setIsUpdatingTeams(false);
});
}
},
@@ -264,7 +262,7 @@ const TeamManagementPage = (): JSX.Element => {
onCancel={toggleCreateTeamModal}
onSubmit={onCreateSubmit}
backendValidators={backendValidators}
- isLoading={teamIsLoading}
+ isUpdatingTeams={isUpdatingTeams}
/>
)}
{showDeleteTeamModal && (
@@ -272,7 +270,7 @@ const TeamManagementPage = (): JSX.Element => {
onCancel={toggleDeleteTeamModal}
onSubmit={onDeleteSubmit}
name={teamEditing?.name || ""}
- isLoading={teamIsRemoving}
+ isUpdatingTeams={isUpdatingTeams}
/>
)}
{showEditTeamModal && (
@@ -281,7 +279,7 @@ const TeamManagementPage = (): JSX.Element => {
onSubmit={onEditSubmit}
defaultName={teamEditing?.name || ""}
backendValidators={backendValidators}
- isLoading={teamIsEditing}
+ isUpdatingTeams={isUpdatingTeams}
/>
)}
diff --git a/frontend/pages/admin/TeamManagementPage/components/CreateTeamModal/CreateTeamModal.tsx b/frontend/pages/admin/TeamManagementPage/components/CreateTeamModal/CreateTeamModal.tsx
index 06556245fa..35fe247739 100644
--- a/frontend/pages/admin/TeamManagementPage/components/CreateTeamModal/CreateTeamModal.tsx
+++ b/frontend/pages/admin/TeamManagementPage/components/CreateTeamModal/CreateTeamModal.tsx
@@ -7,7 +7,6 @@ import Button from "components/buttons/Button";
import InfoBanner from "components/InfoBanner/InfoBanner";
// @ts-ignore
import InputField from "components/forms/fields/InputField";
-import Spinner from "components/Spinner";
const baseClass = "create-team-modal";
@@ -15,14 +14,14 @@ interface ICreateTeamModalProps {
onCancel: () => void;
onSubmit: (formData: ITeamFormData) => void;
backendValidators: { [key: string]: string };
- isLoading: boolean;
+ isUpdatingTeams: boolean;
}
const CreateTeamModal = ({
onCancel,
onSubmit,
backendValidators,
- isLoading,
+ isUpdatingTeams,
}: ICreateTeamModalProps): JSX.Element => {
const [name, setName] = useState("");
const [errors, setErrors] = useState<{ [key: string]: string }>(
@@ -53,43 +52,45 @@ const CreateTeamModal = ({
return (
- {isLoading ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/frontend/pages/admin/TeamManagementPage/components/DeleteTeamModal/DeleteTeamModal.tsx b/frontend/pages/admin/TeamManagementPage/components/DeleteTeamModal/DeleteTeamModal.tsx
index fa3ad467f0..c94b041e70 100644
--- a/frontend/pages/admin/TeamManagementPage/components/DeleteTeamModal/DeleteTeamModal.tsx
+++ b/frontend/pages/admin/TeamManagementPage/components/DeleteTeamModal/DeleteTeamModal.tsx
@@ -1,21 +1,20 @@
-import React, { useEffect } from "react";
+import React from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
-import Spinner from "components/Spinner";
const baseClass = "delete-team-modal";
interface IDeleteTeamModalProps {
name: string;
- isLoading: boolean;
+ isUpdatingTeams: boolean;
onSubmit: () => void;
onCancel: () => void;
}
const DeleteTeamModal = ({
name,
- isLoading,
+ isUpdatingTeams,
onSubmit,
onCancel,
}: IDeleteTeamModalProps): JSX.Element => {
@@ -26,31 +25,31 @@ const DeleteTeamModal = ({
onEnter={onSubmit}
className={baseClass}
>
- {isLoading ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/EditTeamModal.tsx b/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/EditTeamModal.tsx
index c6f8a4effe..69bcf56118 100644
--- a/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/EditTeamModal.tsx
+++ b/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/EditTeamModal.tsx
@@ -6,7 +6,6 @@ import Modal from "components/Modal";
// @ts-ignore
import InputField from "components/forms/fields/InputField";
import Button from "components/buttons/Button";
-import Spinner from "components/Spinner";
const baseClass = "edit-team-modal";
@@ -15,7 +14,7 @@ interface IEditTeamModalProps {
onSubmit: (formData: ITeamFormData) => void;
defaultName: string;
backendValidators: { [key: string]: string };
- isLoading: boolean;
+ isUpdatingTeams: boolean;
}
const EditTeamModal = ({
@@ -23,7 +22,7 @@ const EditTeamModal = ({
onSubmit,
defaultName,
backendValidators,
- isLoading,
+ isUpdatingTeams,
}: IEditTeamModalProps): JSX.Element => {
const [name, setName] = useState(defaultName);
const [errors, setErrors] = useState<{ [key: string]: string }>(
@@ -49,42 +48,35 @@ const EditTeamModal = ({
return (
- {isLoading ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/_styles.scss b/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/_styles.scss
deleted file mode 100644
index 60adcf5f04..0000000000
--- a/frontend/pages/admin/TeamManagementPage/components/EditTeamModal/_styles.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.edit-team-modal {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/CreateUserModal/CreateUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/CreateUserModal/CreateUserModal.tsx
index e880e11688..c917ee31bb 100644
--- a/frontend/pages/admin/UserManagementPage/components/CreateUserModal/CreateUserModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/CreateUserModal/CreateUserModal.tsx
@@ -19,7 +19,7 @@ interface ICreateUserModalProps {
currentTeam?: ITeam;
canUseSso: boolean; // corresponds to whether SSO is enabled for the organization
isModifiedByGlobalAdmin?: boolean | false;
- isLoading?: boolean | false;
+ isUpdatingUsers?: boolean | false;
serverErrors?: { base: string; email: string };
createUserErrors?: IUserFormErrors;
}
@@ -38,36 +38,30 @@ const CreateUserModal = ({
smtpConfigured,
canUseSso,
isModifiedByGlobalAdmin,
- isLoading,
+ isUpdatingUsers,
serverErrors,
createUserErrors,
}: ICreateUserModalProps): JSX.Element => {
return (
- <>
- {isLoading && (
-
-
-
- )}
-
- >
+
);
};
diff --git a/frontend/pages/admin/UserManagementPage/components/CreateUserModal/_styles.scss b/frontend/pages/admin/UserManagementPage/components/CreateUserModal/_styles.scss
deleted file mode 100644
index b9e2d3b058..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/CreateUserModal/_styles.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.create-user-modal {
- .loading-spinner {
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- display: flex;
- align-items: center;
- background: rgba(255, 255, 255, 0.75);
- z-index: 1;
- border-radius: 8px;
- margin: 0;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/DeleteUserForm.tsx b/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/DeleteUserForm.tsx
deleted file mode 100644
index 30de3ec5da..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/DeleteUserForm.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, { useEffect } from "react";
-import Button from "components/buttons/Button";
-
-const baseClass = "delete-user-form";
-
-interface IDeleteUserForm {
- name: string;
- onDelete: () => void;
- onCancel: () => void;
-}
-
-const DeleteUserForm = ({
- name,
- onDelete,
- onCancel,
-}: IDeleteUserForm): JSX.Element => {
- useEffect(() => {
- const listener = (event: KeyboardEvent) => {
- if (event.code === "Enter" || event.code === "NumpadEnter") {
- event.preventDefault();
- onDelete();
- }
- };
-
- document.addEventListener("keydown", listener);
- return () => {
- document.removeEventListener("keydown", listener);
- };
- }, []);
-
- return (
-
-
- You are about to delete{" "}
- {name} from Fleet.
-
-
This action cannot be undone.
-
-
- Delete
-
-
- Cancel
-
-
-
- );
-};
-
-export default DeleteUserForm;
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/_styles.scss b/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/_styles.scss
deleted file mode 100644
index 5d0c8abbe7..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/_styles.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.delete-user-form {
- margin-top: $pad-large;
-
- &__name {
- font-weight: $bold;
- }
-
- &__warning {
- color: $ui-error;
- }
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- height: 38px;
- margin-bottom: 5px;
- margin-left: 15px;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/index.ts b/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/index.ts
deleted file mode 100644
index 7ccf11612d..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/DeleteUserForm/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from "./DeleteUserForm";
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/DeleteUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/DeleteUserModal.tsx
new file mode 100644
index 0000000000..9f581a1461
--- /dev/null
+++ b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/DeleteUserModal.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import Button from "components/buttons/Button";
+import Modal from "components/Modal";
+
+const baseClass = "delete-user-form";
+
+interface IDeleteUserModal {
+ name: string;
+ onDelete: () => void;
+ onCancel: () => void;
+ isUpdatingUsers: boolean;
+}
+
+const DeleteUserModal = ({
+ name,
+ onDelete,
+ onCancel,
+ isUpdatingUsers,
+}: IDeleteUserModal): JSX.Element => {
+ return (
+
+
+
+ You are about to delete{" "}
+ {name} from Fleet.
+
+
This action cannot be undone.
+
+
+ Delete
+
+
+ Cancel
+
+
+
+
+ );
+};
+
+export default DeleteUserModal;
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/_styles.scss b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/_styles.scss
new file mode 100644
index 0000000000..5c32d9b554
--- /dev/null
+++ b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/_styles.scss
@@ -0,0 +1,9 @@
+.delete-user-modal {
+ &__name {
+ font-weight: $bold;
+ }
+
+ &__warning {
+ color: $ui-error;
+ }
+}
diff --git a/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/index.ts b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/index.ts
new file mode 100644
index 0000000000..6eecec5929
--- /dev/null
+++ b/frontend/pages/admin/UserManagementPage/components/DeleteUserModal/index.ts
@@ -0,0 +1 @@
+export { default } from "./DeleteUserModal";
diff --git a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
index b70ac1d269..0d04785381 100644
--- a/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/EditUserModal/EditUserModal.tsx
@@ -24,7 +24,7 @@ interface IEditUserModalProps {
editUserErrors?: IUserFormErrors;
isModifiedByGlobalAdmin?: boolean | false;
isInvitePending?: boolean;
- isLoading: boolean;
+ isUpdatingUsers: boolean;
}
const baseClass = "edit-user-modal";
@@ -46,7 +46,7 @@ const EditUserModal = ({
editUserErrors,
isModifiedByGlobalAdmin,
isInvitePending,
- isLoading,
+ isUpdatingUsers,
}: IEditUserModalProps): JSX.Element => {
return (
- {isLoading ? (
-
- ) : (
-
- )}
+
);
};
diff --git a/frontend/pages/admin/UserManagementPage/components/EditUserModal/_styles.scss b/frontend/pages/admin/UserManagementPage/components/EditUserModal/_styles.scss
deleted file mode 100644
index 460f09a6f3..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/EditUserModal/_styles.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-.edit-user-modal {
- position: relative;
- .loading-spinner {
- width: 100%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- display: flex;
- align-items: center;
- background: rgba(255, 255, 255, 0.75);
- z-index: 1;
- border-radius: 8px;
- margin: 0;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/ResetPasswordModal.tsx b/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/ResetPasswordModal.tsx
index 8e9095d052..d2f665a104 100644
--- a/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/ResetPasswordModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/ResetPasswordModal.tsx
@@ -29,20 +29,15 @@ const ResetPasswordModal = ({
This will revoke all active Fleet API tokens for this user.
-
+
onResetConfirm(user)}
>
Confirm
-
+
Cancel
diff --git a/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/_styles.scss b/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/_styles.scss
deleted file mode 100644
index 77003afbba..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/ResetPasswordModal/_styles.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.reset-password-modal {
- margin-top: $pad-large;
-
- p {
- margin-bottom: 40px;
- font-size: $small;
- }
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- height: 38px;
- margin-bottom: 5px;
- margin-left: 15px;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/ResetSessionsModal.tsx b/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/ResetSessionsModal.tsx
index fff919785a..6af237b636 100644
--- a/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/ResetSessionsModal.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/ResetSessionsModal.tsx
@@ -28,20 +28,15 @@ const ResetSessionsModal = ({
This will revoke all active Fleet API tokens for this user.
-
+
onResetConfirm(user)}
>
Confirm
-
+
Cancel
diff --git a/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/_styles.scss b/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/_styles.scss
deleted file mode 100644
index ef9495c3ca..0000000000
--- a/frontend/pages/admin/UserManagementPage/components/ResetSessionsModal/_styles.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.reset-sessions-modal {
- margin-top: $pad-large;
-
- p {
- margin-bottom: 40px;
- font-size: $small;
- }
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- height: 38px;
- margin-bottom: 5px;
- margin-left: 15px;
- }
-}
diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
index fe09c7d363..f57750fa85 100644
--- a/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UserForm/UserForm.tsx
@@ -85,6 +85,7 @@ interface ICreateUserFormProps {
isInvitePending?: boolean;
serverErrors?: { base: string; email: string }; // "server" because this form does its own client validation
createOrEditUserErrors?: IUserFormErrors;
+ isUpdatingUsers?: boolean;
}
const UserForm = ({
@@ -108,6 +109,7 @@ const UserForm = ({
isInvitePending,
serverErrors,
createOrEditUserErrors,
+ isUpdatingUsers,
}: ICreateUserFormProps): JSX.Element => {
const { renderFlash } = useContext(NotificationContext);
@@ -567,20 +569,18 @@ const UserForm = ({
)}
{!isPremiumTier && renderGlobalRoleForm()}
-
+
{submitText}
-
+
Cancel
diff --git a/frontend/pages/admin/UserManagementPage/components/UserForm/_styles.scss b/frontend/pages/admin/UserManagementPage/components/UserForm/_styles.scss
index 39491bbe9b..efa0d58b6e 100644
--- a/frontend/pages/admin/UserManagementPage/components/UserForm/_styles.scss
+++ b/frontend/pages/admin/UserManagementPage/components/UserForm/_styles.scss
@@ -84,15 +84,6 @@
}
}
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
-
&__invite-admin {
margin-bottom: 8px;
}
diff --git a/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTable.tsx b/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTable.tsx
index df656f4229..22df1a4c53 100644
--- a/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTable.tsx
+++ b/frontend/pages/admin/UserManagementPage/components/UsersTable/UsersTable.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useCallback, useContext, useEffect } from "react";
+import React, { useState, useCallback, useContext } from "react";
import { InjectedRouter } from "react-router";
import { useQuery } from "react-query";
import memoize from "memoize-one";
@@ -22,7 +22,7 @@ import Modal from "components/Modal";
import { DEFAULT_CREATE_USER_ERRORS } from "utilities/constants";
import EmptyUsers from "../EmptyUsers";
import { generateTableHeaders, combineDataSets } from "./UsersTableConfig";
-import DeleteUserForm from "../DeleteUserForm";
+import DeleteUserModal from "../DeleteUserModal";
import ResetPasswordModal from "../ResetPasswordModal";
import ResetSessionsModal from "../ResetSessionsModal";
import { NewUserType } from "../UserForm/UserForm";
@@ -51,8 +51,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
const [showResetSessionsModal, setShowResetSessionsModal] = useState
(
false
);
- const [isLoading, setIsLoading] = useState(false);
- const [isEditingUser, setIsEditingUser] = useState(false);
+ const [isUpdatingUsers, setIsUpdatingUsers] = useState(false);
const [userEditing, setUserEditing] = useState(null);
const [createUserErrors, setCreateUserErrors] = useState(
DEFAULT_CREATE_USER_ERRORS
@@ -210,7 +209,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
};
const onCreateUserSubmit = (formData: any) => {
- setIsLoading(true);
+ setIsUpdatingUsers(true);
if (formData.newUserType === NewUserType.AdminInvited) {
// Do some data formatting adding `invited_by` for the request to be correct and deleteing uncessary fields
@@ -247,7 +246,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
}
})
.finally(() => {
- setIsLoading(false);
+ setIsUpdatingUsers(false);
});
} else {
// Do some data formatting deleting unnecessary fields
@@ -279,7 +278,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
}
})
.finally(() => {
- setIsLoading(false);
+ setIsUpdatingUsers(false);
});
}
};
@@ -296,7 +295,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
const userUpdatedPasswordError = "Password must meet the criteria below";
const userUpdatedError = `Could not edit ${userEditing?.name}. Please try again.`;
- setIsEditingUser(true);
+ setIsUpdatingUsers(true);
if (userEditing.type === "invite") {
return (
userData &&
@@ -323,7 +322,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
}
})
.finally(() => {
- setIsEditingUser(false);
+ setIsUpdatingUsers(false);
})
);
}
@@ -353,12 +352,13 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
}
})
.finally(() => {
- setIsEditingUser(false);
+ setIsUpdatingUsers(false);
})
);
};
const onDeleteUser = () => {
+ setIsUpdatingUsers(true);
if (userEditing.type === "invite") {
invitesAPI
.destroy(userEditing.id)
@@ -374,6 +374,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
.finally(() => {
toggleDeleteUserModal();
refetchInvites();
+ setIsUpdatingUsers(false);
});
} else {
usersAPI
@@ -390,6 +391,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
.finally(() => {
toggleDeleteUserModal();
refetchUsers();
+ setIsUpdatingUsers(false);
});
}
};
@@ -455,7 +457,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
isModifiedByGlobalAdmin
isInvitePending={userEditing.type === "invite"}
editUserErrors={editUserErrors}
- isLoading={isEditingUser}
+ isUpdatingUsers={isUpdatingUsers}
/>
>
@@ -474,7 +476,7 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
isPremiumTier={isPremiumTier || false}
smtpConfigured={config?.smtp_settings.configured || false}
canUseSso={config?.sso_settings.enable_sso || false}
- isLoading={isLoading}
+ isUpdatingUsers={isUpdatingUsers}
isModifiedByGlobalAdmin
/>
);
@@ -482,13 +484,12 @@ const UsersTable = ({ router }: IUsersTableProps): JSX.Element => {
const renderDeleteUserModal = () => {
return (
-
-
-
+
);
};
diff --git a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
index 2101256122..64377c2fd1 100644
--- a/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
@@ -262,6 +262,9 @@ const ManageHostsPage = ({
[key: string]: string;
}>(DEFAULT_CREATE_LABEL_ERRORS);
const [resetPageIndex, setResetPageIndex] = useState(false);
+ const [isUpdatingLabel, setIsUpdatingLabel] = useState(false);
+ const [isUpdatingSecret, setIsUpdatingSecret] = useState(false);
+ const [isUpdatingHosts, setIsUpdatingHosts] = useState(false);
// ======== end states
@@ -294,13 +297,18 @@ const ManageHostsPage = ({
const canEnrollGlobalHosts = isGlobalAdmin || isGlobalMaintainer;
const canAddNewLabels = (isGlobalAdmin || isGlobalMaintainer) ?? false;
- const { data: labels, error: labelsError, refetch: refetchLabels } = useQuery<
- ILabelsResponse,
- Error,
- ILabel[]
- >(["labels"], () => labelsAPI.loadAll(), {
- select: (data: ILabelsResponse) => data.labels,
- });
+ const {
+ isLoading: isLoadingLabels,
+ data: labels,
+ error: labelsError,
+ refetch: refetchLabels,
+ } = useQuery(
+ ["labels"],
+ () => labelsAPI.loadAll(),
+ {
+ select: (data: ILabelsResponse) => data.labels,
+ }
+ );
const {
isLoading: isGlobalSecretsLoading,
@@ -884,6 +892,8 @@ const ManageHostsPage = ({
newSecrets.push({ secret: enrollSecretString });
}
+ setIsUpdatingSecret(true);
+
try {
if (currentTeam?.id) {
await enrollSecretsAPI.modifyTeamEnrollSecrets(
@@ -918,6 +928,8 @@ const ManageHostsPage = ({
selectedSecret ? "edit" : "add"
} enroll secret. Please try again.`
);
+ } finally {
+ setIsUpdatingSecret(false);
}
};
@@ -933,6 +945,8 @@ const ManageHostsPage = ({
(s) => s.secret !== selectedSecret?.secret
);
+ setIsUpdatingSecret(true);
+
try {
if (currentTeam?.id) {
await enrollSecretsAPI.modifyTeamEnrollSecrets(
@@ -958,6 +972,8 @@ const ManageHostsPage = ({
} catch (error) {
console.error(error);
renderFlash("error", "Could not delete enroll secret. Please try again.");
+ } finally {
+ setIsUpdatingSecret(false);
}
};
@@ -968,6 +984,7 @@ const ManageHostsPage = ({
}
const updateAttrs = deepDifference(formData, selectedLabel);
+ setIsUpdatingLabel(true);
labelsAPI
.update(selectedLabel, updateAttrs)
@@ -1003,6 +1020,9 @@ const ManageHostsPage = ({
} else {
renderFlash("error", "Could not create label. Please try again.");
}
+ })
+ .finally(() => {
+ setIsUpdatingLabel(false);
});
};
@@ -1011,6 +1031,7 @@ const ManageHostsPage = ({
};
const onSaveAddLabel = (formData: ILabelFormData) => {
+ setIsUpdatingLabel(true);
labelsAPI
.create(formData)
.then(() => {
@@ -1046,6 +1067,9 @@ const ManageHostsPage = ({
} else {
renderFlash("error", "Could not create label. Please try again.");
}
+ })
+ .finally(() => {
+ setIsUpdatingLabel(false);
});
};
@@ -1061,6 +1085,7 @@ const ManageHostsPage = ({
console.error("Label isn't available. This should not happen.");
return false;
}
+ setIsUpdatingLabel(true);
const { MANAGE_HOSTS } = PATHS;
try {
@@ -1076,9 +1101,12 @@ const ManageHostsPage = ({
queryParams,
})
);
+ renderFlash("success", "Successfully deleted label.");
} catch (error) {
console.error(error);
renderFlash("error", "Could not delete label. Please try again.");
+ } finally {
+ setIsUpdatingLabel(false);
}
};
@@ -1093,6 +1121,8 @@ const ManageHostsPage = ({
};
const onTransferHostSubmit = async (team: ITeam) => {
+ setIsUpdatingHosts(true);
+
const teamId = typeof team.id === "number" ? team.id : null;
let action = hostsAPI.transferToTeam(teamId, selectedHostIds);
@@ -1145,10 +1175,14 @@ const ManageHostsPage = ({
setIsAllMatchingHostsSelected(false);
} catch (error) {
renderFlash("error", "Could not transfer hosts. Please try again.");
+ } finally {
+ setIsUpdatingHosts(false);
}
};
const onDeleteHostSubmit = async () => {
+ setIsUpdatingHosts(true);
+
let action = hostsAPI.destroyBulk(selectedHostIds);
if (isAllMatchingHostsSelected) {
@@ -1201,6 +1235,8 @@ const ManageHostsPage = ({
selectedHostIds.length === 1 ? "host" : "hosts"
}. Please try again.`
);
+ } finally {
+ setIsUpdatingHosts(false);
}
};
@@ -1427,6 +1463,7 @@ const ManageHostsPage = ({
onSaveSecret={onSaveSecret}
toggleSecretEditorModal={toggleSecretEditorModal}
selectedSecret={selectedSecret}
+ isUpdatingSecret={isUpdatingSecret}
/>
);
@@ -1436,6 +1473,7 @@ const ManageHostsPage = ({
selectedTeam={currentTeam?.id || 0}
teams={teams || []}
toggleDeleteSecretModal={toggleDeleteSecretModal}
+ isUpdatingSecret={isUpdatingSecret}
/>
);
@@ -1455,6 +1493,7 @@ const ManageHostsPage = ({
);
@@ -1489,6 +1528,7 @@ const ManageHostsPage = ({
teams={teams}
onSubmit={onTransferHostSubmit}
onCancel={toggleTransferHostModal}
+ isUpdatingHosts={isUpdatingHosts}
/>
);
};
@@ -1499,6 +1539,7 @@ const ManageHostsPage = ({
onSubmit={onDeleteHostSubmit}
onCancel={toggleDeleteHostModal}
isAllMatchingHostsSelected={isAllMatchingHostsSelected}
+ isUpdatingHosts={isUpdatingHosts}
/>
);
@@ -1684,6 +1725,7 @@ const ManageHostsPage = ({
handleSubmit={onSaveAddLabel}
baseError={labelsError?.message || ""}
backendValidators={labelValidator}
+ isUpdatingLabel={isUpdatingLabel}
/>
);
}
@@ -1697,6 +1739,7 @@ const ManageHostsPage = ({
handleSubmit={onEditLabel}
baseError={labelsError?.message || ""}
backendValidators={labelValidator}
+ isUpdatingLabel={isUpdatingLabel}
isEdit
/>
);
@@ -1860,14 +1903,14 @@ const ManageHostsPage = ({
-
setShowNoEnrollSecretBanner(!showNoEnrollSecretBanner)
}
>
-
+
)
@@ -1912,8 +1955,9 @@ const ManageHostsPage = ({
filteredHostCount === 0
) && (
Add hosts
diff --git a/frontend/pages/hosts/ManageHostsPage/_styles.scss b/frontend/pages/hosts/ManageHostsPage/_styles.scss
index 9fcbac6a4a..ee4d847cac 100644
--- a/frontend/pages/hosts/ManageHostsPage/_styles.scss
+++ b/frontend/pages/hosts/ManageHostsPage/_styles.scss
@@ -197,16 +197,6 @@
}
}
- &__modal-buttons {
- width: 100%;
- display: flex;
- justify-content: flex-end;
-
- .button:first-child {
- margin-right: $pad-medium;
- }
- }
-
.form-field--dropdown {
margin: 0;
}
diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/DeleteHostModal.tsx b/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/DeleteHostModal.tsx
index 54b117036d..f2a9758d3f 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/DeleteHostModal.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/DeleteHostModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
@@ -10,6 +10,7 @@ interface IDeleteHostModalProps {
onSubmit: () => void;
onCancel: () => void;
isAllMatchingHostsSelected: boolean;
+ isUpdatingHosts: boolean;
}
const DeleteHostModal = ({
@@ -17,6 +18,7 @@ const DeleteHostModal = ({
onSubmit,
onCancel,
isAllMatchingHostsSelected,
+ isUpdatingHosts,
}: IDeleteHostModalProps): JSX.Element => {
return (
-
+
Delete
-
+
Cancel
diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/_styles.scss b/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/_styles.scss
deleted file mode 100644
index ca38d2d780..0000000000
--- a/frontend/pages/hosts/ManageHostsPage/components/DeleteHostModal/_styles.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.delete-host-modal {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx
index 61fcf59048..29086ee6e4 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/DeleteLabelModal/DeleteLabelModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React from "react";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
@@ -8,11 +8,13 @@ const baseClass = "delete-label-modal";
interface IDeleteLabelModalProps {
onSubmit: () => void;
onCancel: () => void;
+ isUpdatingLabel: boolean;
}
const DeleteLabelModal = ({
onSubmit,
onCancel,
+ isUpdatingLabel,
}: IDeleteLabelModalProps): JSX.Element => {
return (
Are you sure you wish to delete this label?
+
+ Delete
+
Cancel
-
- Delete
-
>
diff --git a/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx b/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx
index be1fff5b33..e3bcade400 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/EditColumnsModal/EditColumnsModal.jsx
@@ -82,16 +82,15 @@ const EditColumnsModal = ({
})}
-
- Cancel
-
onSaveColumns(getHiddenColumns(columnItems))}
variant={"default"}
>
Save
+
+ Cancel
+
>
diff --git a/frontend/pages/hosts/ManageHostsPage/components/LabelForm/LabelForm.tsx b/frontend/pages/hosts/ManageHostsPage/components/LabelForm/LabelForm.tsx
index ab4fd12533..9ea4020b33 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/LabelForm/LabelForm.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/LabelForm/LabelForm.tsx
@@ -21,6 +21,7 @@ interface ILabelFormProps {
handleSubmit: (formData: ILabelFormData) => void;
onOsqueryTableSelect?: (tableName: string) => void;
backendValidators: { [key: string]: string };
+ isUpdatingLabel?: boolean;
}
const baseClass = "label-form";
@@ -60,6 +61,7 @@ const LabelForm = ({
handleSubmit,
onOsqueryTableSelect,
backendValidators,
+ isUpdatingLabel,
}: ILabelFormProps): JSX.Element => {
const [name, setName] = useState
(selectedLabel?.name || "");
const [nameError, setNameError] = useState("");
@@ -163,6 +165,7 @@ const LabelForm = ({
selectedLabel && selectedLabel.label_membership_type === "manual";
const headerText = isEdit ? "Edit label" : "New label";
const saveBtnText = isEdit ? "Update label" : "Save label";
+ const saveBtnClass = isEdit ? "update-label-loading" : "save-label-loading";
const aceHintText = isEdit
? "Label queries are immutable. To change the query, delete this label and create a new one."
: "";
@@ -240,18 +243,15 @@ const LabelForm = ({
)}
-
-
+
+
Cancel
{saveBtnText}
diff --git a/frontend/pages/hosts/ManageHostsPage/components/LabelForm/_styles.scss b/frontend/pages/hosts/ManageHostsPage/components/LabelForm/_styles.scss
index 4841cd4ed6..23d688f4fd 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/LabelForm/_styles.scss
+++ b/frontend/pages/hosts/ManageHostsPage/components/LabelForm/_styles.scss
@@ -26,13 +26,11 @@
}
}
- .query-form__run-query-btn,
- .query-form__stop-query-btn {
- margin-left: $pad-xsmall;
- }
-
- &__cancel-btn {
- margin-right: $pad-medium;
+ &__button-wrap {
+ margin: 0;
+ margin-top: $pad-large;
+ display: flex;
+ gap: $pad-medium;
}
&__text-editor-wrapper {
diff --git a/frontend/pages/hosts/ManageHostsPage/components/NoHosts/NoHosts.tsx b/frontend/pages/hosts/ManageHostsPage/components/NoHosts/NoHosts.tsx
index 47c97d409f..690f7fc457 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/NoHosts/NoHosts.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/NoHosts/NoHosts.tsx
@@ -38,11 +38,7 @@ const NoHosts = ({
Add your devices to Fleet
Generate an installer to add your own devices.
-
+
Add hosts
diff --git a/frontend/pages/hosts/ManageHostsPage/components/TransferHostModal/TransferHostModal.tsx b/frontend/pages/hosts/ManageHostsPage/components/TransferHostModal/TransferHostModal.tsx
index 065605a961..beb23206ff 100644
--- a/frontend/pages/hosts/ManageHostsPage/components/TransferHostModal/TransferHostModal.tsx
+++ b/frontend/pages/hosts/ManageHostsPage/components/TransferHostModal/TransferHostModal.tsx
@@ -15,6 +15,7 @@ interface ITransferHostModal {
teams: ITeam[];
onSubmit: (team: ITeam) => void;
onCancel: () => void;
+ isUpdatingHosts: boolean;
}
interface INoTeamOption {
@@ -33,6 +34,7 @@ const TransferHostModal = ({
onSubmit,
teams,
isGlobalAdmin,
+ isUpdatingHosts,
}: ITransferHostModal): JSX.Element => {
const [selectedTeam, setSelectedTeam] = useState
();
@@ -83,17 +85,19 @@ const TransferHostModal = ({
) : null}
-
- Cancel
-
Transfer
+
+ Cancel
+
diff --git a/frontend/pages/hosts/details/DeviceUserPage/InfoModal/InfoModal.tsx b/frontend/pages/hosts/details/DeviceUserPage/InfoModal/InfoModal.tsx
index d1442b3853..47fe6c92f2 100644
--- a/frontend/pages/hosts/details/DeviceUserPage/InfoModal/InfoModal.tsx
+++ b/frontend/pages/hosts/details/DeviceUserPage/InfoModal/InfoModal.tsx
@@ -36,13 +36,8 @@ const InfoModal = ({ onCancel }: IInfoModalProps): JSX.Element => {
-
-
+
+
Ok
diff --git a/frontend/pages/hosts/details/DeviceUserPage/InfoModal/_styles.scss b/frontend/pages/hosts/details/DeviceUserPage/InfoModal/_styles.scss
index a5b77cfb6a..46d7844223 100644
--- a/frontend/pages/hosts/details/DeviceUserPage/InfoModal/_styles.scss
+++ b/frontend/pages/hosts/details/DeviceUserPage/InfoModal/_styles.scss
@@ -19,13 +19,4 @@
margin: 0;
}
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- width: 120px;
- }
}
diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
index b67b4b8529..714fc98f58 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
@@ -140,6 +140,7 @@ const HostDetailsPage = ({
const [selectedPolicy, setSelectedPolicy] = useState(
null
);
+ const [isUpdatingHost, setIsUpdatingHost] = useState(false);
const [refetchStartTime, setRefetchStartTime] = useState(null);
const [showRefetchSpinner, setShowRefetchSpinner] = useState(false);
@@ -389,6 +390,7 @@ const HostDetailsPage = ({
const onDestroyHost = async () => {
if (host) {
+ setIsUpdatingHost(true);
try {
await hostAPI.destroy(host);
renderFlash(
@@ -401,6 +403,7 @@ const HostDetailsPage = ({
renderFlash("error", `Host "${host.hostname}" could not be deleted.`);
} finally {
setShowDeleteHostModal(false);
+ setIsUpdatingHost(false);
}
}
};
@@ -447,6 +450,8 @@ const HostDetailsPage = ({
};
const onTransferHostSubmit = async (team: ITeam) => {
+ setIsUpdatingHost(true);
+
const teamId = typeof team.id === "number" ? team.id : null;
try {
@@ -463,6 +468,8 @@ const HostDetailsPage = ({
} catch (error) {
console.log(error);
renderFlash("error", "Could not transfer host. Please try again.");
+ } finally {
+ setIsUpdatingHost(false);
}
};
@@ -633,6 +640,7 @@ const HostDetailsPage = ({
onCancel={() => setShowDeleteHostModal(false)}
onSubmit={onDestroyHost}
hostName={host?.hostname}
+ isUpdatingHost={isUpdatingHost}
/>
)}
{showQueryHostModal && host && (
@@ -651,6 +659,7 @@ const HostDetailsPage = ({
onSubmit={onTransferHostSubmit}
teams={teams || []}
isGlobalAdmin={isGlobalAdmin as boolean}
+ isUpdatingHost={isUpdatingHost}
/>
)}
{!!host && showPolicyDetailsModal && (
diff --git a/frontend/pages/hosts/details/HostDetailsPage/_styles.scss b/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
index 2e48bcd5c3..39afb2768e 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
+++ b/frontend/pages/hosts/details/HostDetailsPage/_styles.scss
@@ -344,11 +344,7 @@
&__action-button-container {
display: flex;
align-items: center;
- }
-
- &__query-button,
- &__transfer-button {
- margin-right: $pad-large;
+ gap: $pad-large;
}
&__back-link {
diff --git a/frontend/pages/hosts/details/HostDetailsPage/modals/DeleteHostModal/DeleteHostModal.tsx b/frontend/pages/hosts/details/HostDetailsPage/modals/DeleteHostModal/DeleteHostModal.tsx
index f5851e2882..20e5dd5175 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/modals/DeleteHostModal/DeleteHostModal.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/modals/DeleteHostModal/DeleteHostModal.tsx
@@ -8,6 +8,7 @@ interface IDeleteHostModal {
onSubmit: (team: ITeam) => void;
onCancel: () => void;
hostName?: string;
+ isUpdatingHost: boolean;
}
const baseClass = "delete-host-modal";
@@ -16,6 +17,7 @@ const DeleteHostModal = ({
onCancel,
onSubmit,
hostName,
+ isUpdatingHost,
}: IDeleteHostModal): JSX.Element => {
return (
+
+ Delete
+
Cancel
-
- Delete
-
>
diff --git a/frontend/pages/hosts/details/HostDetailsPage/modals/TransferHostModal/TransferHostModal.tsx b/frontend/pages/hosts/details/HostDetailsPage/modals/TransferHostModal/TransferHostModal.tsx
index bae21a91e6..aeda0d8cab 100644
--- a/frontend/pages/hosts/details/HostDetailsPage/modals/TransferHostModal/TransferHostModal.tsx
+++ b/frontend/pages/hosts/details/HostDetailsPage/modals/TransferHostModal/TransferHostModal.tsx
@@ -13,6 +13,7 @@ interface ITransferHostModal {
teams: ITeam[];
onSubmit: (team: ITeam) => void;
onCancel: () => void;
+ isUpdatingHost: boolean;
}
interface INoTeamOption {
@@ -31,6 +32,7 @@ const TransferHostModal = ({
onSubmit,
teams,
isGlobalAdmin,
+ isUpdatingHost,
}: ITransferHostModal): JSX.Element => {
const [selectedTeam, setSelectedTeam] = useState();
@@ -81,17 +83,19 @@ const TransferHostModal = ({
)}
-
- Cancel
-
Transfer
+
+ Cancel
+
diff --git a/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/PolicyDetailsModal.tsx b/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/PolicyDetailsModal.tsx
index a50b19332c..b230751c28 100644
--- a/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/PolicyDetailsModal.tsx
+++ b/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/PolicyDetailsModal.tsx
@@ -29,12 +29,8 @@ const PolicyDetailsModal = ({
{policy?.resolution}
)}
-
-
+
+
Done
diff --git a/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/_styles.scss b/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/_styles.scss
index bec932df7b..3c48165687 100644
--- a/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/_styles.scss
+++ b/frontend/pages/hosts/details/cards/Policies/HostPoliciesTable/PolicyDetailsModal/_styles.scss
@@ -9,16 +9,6 @@
font-weight: $bold;
}
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-large;
- }
-
- &__btn {
- margin-left: 12px;
- }
-
.policy-details-modal__resolution {
overflow: hidden;
}
diff --git a/frontend/pages/packs/EditPackPage/EditPackPage.tsx b/frontend/pages/packs/EditPackPage/EditPackPage.tsx
index 4e15e592c3..8d07ffadf7 100644
--- a/frontend/pages/packs/EditPackPage/EditPackPage.tsx
+++ b/frontend/pages/packs/EditPackPage/EditPackPage.tsx
@@ -102,6 +102,7 @@ const EditPacksPage = ({
const [selectedPackQueryIds, setSelectedPackQueryIds] = useState<
number[] | never[]
>([]);
+ const [isUpdatingPack, setIsUpdatingPack] = useState(false);
const packTargets = storedPack
? [
@@ -154,6 +155,7 @@ const EditPacksPage = ({
};
const handlePackFormSubmit = (formData: IFormData) => {
+ setIsUpdatingPack(true);
const updatedPack = deepDifference(formData, storedPack);
packsAPI
.update(packId, updatedPack)
@@ -173,6 +175,9 @@ const EditPacksPage = ({
} else {
renderFlash("error", `Could not update pack. Please try again.`);
}
+ })
+ .finally(() => {
+ setIsUpdatingPack(false);
});
};
@@ -180,6 +185,7 @@ const EditPacksPage = ({
formData: IPackQueryFormData,
editQuery: IScheduledQuery | undefined
) => {
+ setIsUpdatingPack(true);
const request = editQuery
? scheduledQueriesAPI.update(editQuery, formData)
: scheduledQueriesAPI.create(formData);
@@ -193,11 +199,13 @@ const EditPacksPage = ({
.finally(() => {
togglePackQueryEditorModal();
refetchStoredPackQueries();
+ setIsUpdatingPack(false);
});
return false;
};
const onRemovePackQuerySubmit = () => {
+ setIsUpdatingPack(true);
const queryOrQueries =
selectedPackQueryIds.length === 1 ? "query" : "queries";
@@ -221,6 +229,7 @@ const EditPacksPage = ({
.finally(() => {
toggleRemovePackQueryModal();
refetchStoredPackQueries();
+ setIsUpdatingPack(false);
});
};
@@ -245,6 +254,7 @@ const EditPacksPage = ({
onRemovePackQueries={onRemovePackQueriesClick}
scheduledQueries={storedPackQueries}
isLoadingPackQueries={isStoredPackQueriesLoading}
+ isUpdatingPack={isUpdatingPack}
/>
)}
{showPackQueryEditorModal && fleetQueries && (
@@ -254,6 +264,7 @@ const EditPacksPage = ({
allQueries={fleetQueries}
editQuery={selectedPackQuery}
packId={packId}
+ isUpdatingPack={isUpdatingPack}
/>
)}
{showRemovePackQueryModal && fleetQueries && (
@@ -262,6 +273,7 @@ const EditPacksPage = ({
onSubmit={onRemovePackQuerySubmit}
selectedQuery={selectedPackQuery}
selectedQueryIds={selectedPackQueryIds}
+ isUpdatingPack={isUpdatingPack}
/>
)}
diff --git a/frontend/pages/packs/EditPackPage/components/PackQueryEditorModal/PackQueryEditorModal.tsx b/frontend/pages/packs/EditPackPage/components/PackQueryEditorModal/PackQueryEditorModal.tsx
index 625423048c..9995b6cee5 100644
--- a/frontend/pages/packs/EditPackPage/components/PackQueryEditorModal/PackQueryEditorModal.tsx
+++ b/frontend/pages/packs/EditPackPage/components/PackQueryEditorModal/PackQueryEditorModal.tsx
@@ -41,6 +41,7 @@ interface IPackQueryEditorModalProps {
) => void;
editQuery?: IScheduledQuery;
packId: number;
+ isUpdatingPack: boolean;
}
interface INoQueryOption {
id: number;
@@ -63,6 +64,7 @@ const PackQueryEditorModal = ({
allQueries,
editQuery,
packId,
+ isUpdatingPack,
}: IPackQueryEditorModalProps): JSX.Element => {
const [selectedQuery, setSelectedQuery] = useState<
IScheduledQuery | INoQueryOption
@@ -242,17 +244,19 @@ const PackQueryEditorModal = ({
/>
-
- Cancel
-
{editQuery?.name ? "Save" : "Add query"}
+
+ Cancel
+
diff --git a/frontend/pages/packs/EditPackPage/components/RemovePackQueryModal/RemovePackQueryModal.tsx b/frontend/pages/packs/EditPackPage/components/RemovePackQueryModal/RemovePackQueryModal.tsx
index cded026e7c..b90e90e064 100644
--- a/frontend/pages/packs/EditPackPage/components/RemovePackQueryModal/RemovePackQueryModal.tsx
+++ b/frontend/pages/packs/EditPackPage/components/RemovePackQueryModal/RemovePackQueryModal.tsx
@@ -11,6 +11,7 @@ interface IRemovePackQueryModalProps {
onSubmit: () => void;
selectedQuery?: IScheduledQuery;
selectedQueryIds: number[];
+ isUpdatingPack: boolean;
}
const RemovePackQueryModal = ({
@@ -18,6 +19,7 @@ const RemovePackQueryModal = ({
onSubmit,
selectedQuery,
selectedQueryIds,
+ isUpdatingPack,
}: IRemovePackQueryModalProps): JSX.Element => {
const queryOrQueries =
selectedQuery || selectedQueryIds?.length === 1 ? "query" : "queries";
@@ -32,12 +34,18 @@ const RemovePackQueryModal = ({
Are you sure you want to remove the selected {queryOrQueries} from your
pack?
+
+ Remove
+
Cancel
-
- Remove
-
diff --git a/frontend/pages/packs/ManagePacksPage/ManagePacksPage.tsx b/frontend/pages/packs/ManagePacksPage/ManagePacksPage.tsx
index feac5e715b..d66005e220 100644
--- a/frontend/pages/packs/ManagePacksPage/ManagePacksPage.tsx
+++ b/frontend/pages/packs/ManagePacksPage/ManagePacksPage.tsx
@@ -64,6 +64,7 @@ const ManagePacksPage = ({ router }: IManagePacksPageProps): JSX.Element => {
const [showDeletePackModal, setShowDeletePackModal] = useState(
false
);
+ const [isUpdatingPack, setIsUpdatingPack] = useState(false);
const {
data: packs,
@@ -91,6 +92,7 @@ const ManagePacksPage = ({ router }: IManagePacksPageProps): JSX.Element => {
};
const onDeletePackSubmit = useCallback(() => {
+ setIsUpdatingPack(true);
const packOrPacks = selectedPackIds.length === 1 ? "pack" : "packs";
const promises = selectedPackIds.map((id: number) => {
@@ -110,6 +112,7 @@ const ManagePacksPage = ({ router }: IManagePacksPageProps): JSX.Element => {
.finally(() => {
refetchPacks();
toggleDeletePackModal();
+ setIsUpdatingPack(false);
});
}, [refetchPacks, selectedPackIds, toggleDeletePackModal]);
@@ -200,6 +203,7 @@ const ManagePacksPage = ({ router }: IManagePacksPageProps): JSX.Element => {
)}
diff --git a/frontend/pages/packs/ManagePacksPage/components/DeletePackModal/DeletePackModal.tsx b/frontend/pages/packs/ManagePacksPage/components/DeletePackModal/DeletePackModal.tsx
index b9e622366d..f65fffe9f5 100644
--- a/frontend/pages/packs/ManagePacksPage/components/DeletePackModal/DeletePackModal.tsx
+++ b/frontend/pages/packs/ManagePacksPage/components/DeletePackModal/DeletePackModal.tsx
@@ -8,11 +8,13 @@ const baseClass = "remove-pack-modal";
interface IDeletePackModalProps {
onCancel: () => void;
onSubmit: () => void;
+ isUpdatingPack: boolean;
}
const DeletePackModal = ({
onCancel,
onSubmit,
+ isUpdatingPack,
}: IDeletePackModalProps): JSX.Element => {
return (
- Cancel
-
-
Delete
+
+ Cancel
+
diff --git a/frontend/pages/packs/PackComposerPage/PackComposerPage.tsx b/frontend/pages/packs/PackComposerPage/PackComposerPage.tsx
index 5555735803..6c53f2d77a 100644
--- a/frontend/pages/packs/PackComposerPage/PackComposerPage.tsx
+++ b/frontend/pages/packs/PackComposerPage/PackComposerPage.tsx
@@ -29,6 +29,7 @@ const PackComposerPage = ({ router }: IPackComposerPageProps): JSX.Element => {
const { renderFlash } = useContext(NotificationContext);
const [selectedTargetsCount, setSelectedTargetsCount] = useState
(0);
+ const [isUpdatingPack, setIsUpdatingPack] = useState(false);
const onFetchTargets = (
query: IQuery,
@@ -42,6 +43,8 @@ const PackComposerPage = ({ router }: IPackComposerPageProps): JSX.Element => {
const handleSubmit = async (formData: IEditPackFormData) => {
const { create } = packsAPI;
+ setIsUpdatingPack(true);
+
try {
const {
pack: { id: packID },
@@ -62,6 +65,8 @@ const PackComposerPage = ({ router }: IPackComposerPageProps): JSX.Element => {
} else {
renderFlash("error", "Unable to create pack.");
}
+ } finally {
+ setIsUpdatingPack(false);
}
};
@@ -74,6 +79,7 @@ const PackComposerPage = ({ router }: IPackComposerPageProps): JSX.Element => {
onFetchTargets={onFetchTargets}
selectedTargetsCount={selectedTargetsCount}
isPremiumTier={isPremiumTier}
+ isUpdatingPack={isUpdatingPack}
/>
diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
index ec129d919c..ad3fd57c3b 100644
--- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx
@@ -28,7 +28,7 @@ import MainContent from "components/MainContent";
import PoliciesTable from "./components/PoliciesTable";
import ManageAutomationsModal from "./components/ManageAutomationsModal";
import AddPolicyModal from "./components/AddPolicyModal";
-import DeletePoliciesModal from "./components/DeletePoliciesModal";
+import DeletePolicyModal from "./components/DeletePolicyModal";
interface IManagePoliciesPageProps {
router: InjectedRouter;
@@ -75,18 +75,17 @@ const ManagePolicyPage = ({
} = useContext(PolicyContext);
const { setResetSelectedRows } = useContext(TableContext);
-
- const [isAutomationsLoading, setIsAutomationsLoading] = useState(
+ const [isUpdatingAutomations, setIsUpdatingAutomations] = useState(
false
);
- const [isDeletingPolicy, setIsDeletingPolicy] = useState(false);
+ const [isUpdatingPolicies, setIsUpdatingPolicies] = useState(false);
const [selectedPolicyIds, setSelectedPolicyIds] = useState([]);
const [showManageAutomationsModal, setShowManageAutomationsModal] = useState(
false
);
const [showPreviewPayloadModal, setShowPreviewPayloadModal] = useState(false);
const [showAddPolicyModal, setShowAddPolicyModal] = useState(false);
- const [showDeletePoliciesModal, setShowDeletePoliciesModal] = useState(false);
+ const [showDeletePolicyModal, setShowDeletePolicyModal] = useState(false);
const [showInheritedPolicies, setShowInheritedPolicies] = useState(false);
useEffect(() => {
@@ -197,8 +196,8 @@ const ManagePolicyPage = ({
const toggleAddPolicyModal = () => setShowAddPolicyModal(!showAddPolicyModal);
- const toggleDeletePoliciesModal = () =>
- setShowDeletePoliciesModal(!showDeletePoliciesModal);
+ const toggleDeletePolicyModal = () =>
+ setShowDeletePolicyModal(!showDeletePolicyModal);
const toggleShowInheritedPolicies = () =>
setShowInheritedPolicies(!showInheritedPolicies);
@@ -206,7 +205,7 @@ const ManagePolicyPage = ({
const handleUpdateAutomations = async (
requestBody: IAutomationsConfig | ITeamAutomationsConfig
) => {
- setIsAutomationsLoading(true);
+ setIsUpdatingAutomations(true);
try {
await (teamId
? teamsAPI.update(requestBody, teamId)
@@ -219,7 +218,7 @@ const ManagePolicyPage = ({
);
} finally {
toggleManageAutomationsModal();
- setIsAutomationsLoading(false);
+ setIsUpdatingAutomations(false);
refetchConfig();
teamId && refetchTeamConfig();
}
@@ -232,14 +231,14 @@ const ManagePolicyPage = ({
toggleAddPolicyModal();
};
- const onDeletePoliciesClick = (selectedTableIds: number[]): void => {
- toggleDeletePoliciesModal();
+ const onDeletePolicyClick = (selectedTableIds: number[]): void => {
+ toggleDeletePolicyModal();
setSelectedPolicyIds(selectedTableIds);
};
- const onDeletePoliciesSubmit = async () => {
+ const onDeletePolicySubmit = async () => {
const id = currentTeam?.id;
- setIsDeletingPolicy(true);
+ setIsUpdatingPolicies(true);
try {
const request = id
? teamPoliciesAPI.destroy(id, selectedPolicyIds)
@@ -263,8 +262,8 @@ const ManagePolicyPage = ({
}. Please try again.`
);
} finally {
- toggleDeletePoliciesModal();
- setIsDeletingPolicy(false);
+ toggleDeletePolicyModal();
+ setIsUpdatingPolicies(false);
}
};
@@ -426,7 +425,7 @@ const ManagePolicyPage = ({
isFetchingConfig
}
onAddPolicyClick={onAddPolicyClick}
- onDeletePoliciesClick={onDeletePoliciesClick}
+ onDeletePolicyClick={onDeletePolicyClick}
canAddOrDeletePolicy={canAddOrDeletePolicy}
currentTeam={currentTeam}
currentAutomatedPolicies={currentAutomatedPolicies}
@@ -442,7 +441,7 @@ const ManagePolicyPage = ({
policiesList={globalPolicies || []}
isLoading={isFetchingGlobalPolicies || isFetchingConfig}
onAddPolicyClick={onAddPolicyClick}
- onDeletePoliciesClick={onDeletePoliciesClick}
+ onDeletePolicyClick={onDeletePolicyClick}
canAddOrDeletePolicy={canAddOrDeletePolicy}
currentTeam={currentTeam}
currentAutomatedPolicies={currentAutomatedPolicies}
@@ -478,7 +477,7 @@ const ManagePolicyPage = ({
)}
- {showDeletePoliciesModal && (
-
)}
diff --git a/frontend/pages/policies/ManagePoliciesPage/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/_styles.scss
index 8bd9426c88..6339b46856 100644
--- a/frontend/pages/policies/ManagePoliciesPage/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/_styles.scss
@@ -107,16 +107,6 @@
}
}
- &__modal-buttons {
- width: 100%;
- display: flex;
- justify-content: flex-end;
-
- .button:first-child {
- margin-right: $pad-medium;
- }
- }
-
&__add-policy-link {
transition: color 150ms ease-in-out, background 150ms ease-in-out,
top 50ms ease-in-out, box-shadow 50ms ease-in-out, border 50ms ease-in-out;
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/AddPolicyModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/AddPolicyModal.tsx
index e5024e20f6..cd612531ca 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/AddPolicyModal.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/AddPolicyModal.tsx
@@ -72,18 +72,10 @@ const AddPolicyModal = ({
});
return (
-
+
<>
Choose a policy template to get started or{" "}
-
+
create your own policy
.
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/_styles.scss
index 1a7bb3b8fe..95245cd125 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/components/AddPolicyModal/_styles.scss
@@ -1,62 +1,27 @@
.add-policy-modal {
- #error-icon {
- height: 12px;
- width: 12px;
- margin-right: 8px;
- }
+ @include position(absolute, 22px null null null);
+ background-color: $core-white;
+ width: 658px;
+ padding: $pad-xxlarge;
+ border-radius: $pad-small;
- #new-tab-icon {
- height: 12px;
- width: 12px;
- margin-left: 6px;
- }
+ .info {
+ display: flex;
- a {
- font-size: $x-small;
- color: $core-vibrant-blue;
- font-weight: $bold;
- text-decoration: none;
- }
-
- &__modal {
- @include position(absolute, 22px null null null);
- background-color: $core-white;
- width: 658px;
- padding: $pad-xxlarge;
- border-radius: $pad-small;
-
- a {
- font-size: $x-small;
- color: $core-vibrant-blue;
+ &__header {
+ display: block;
+ color: $core-fleet-black;
font-weight: $bold;
- text-decoration: none;
- }
-
- .button--unstyled {
font-size: $x-small;
- color: $core-vibrant-blue;
- font-weight: $bold;
- text-decoration: none;
+ text-align: left;
}
-
- .info {
- display: flex;
-
- &__header {
- display: block;
- color: $core-fleet-black;
- font-weight: $bold;
- font-size: $x-small;
- text-align: left;
- }
- &__data {
- display: block;
- color: $core-fleet-black;
- font-weight: normal;
- font-size: $x-small;
- text-align: left;
- margin-top: 10px;
- }
+ &__data {
+ display: block;
+ color: $core-fleet-black;
+ font-weight: normal;
+ font-size: $x-small;
+ text-align: left;
+ margin-top: 10px;
}
}
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/DeletePoliciesModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/DeletePoliciesModal.tsx
deleted file mode 100644
index 6a25606892..0000000000
--- a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/DeletePoliciesModal.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from "react";
-
-import Modal from "components/Modal";
-import Button from "components/buttons/Button";
-import Spinner from "components/Spinner";
-
-const baseClass = "delete-policies-modal";
-
-interface IDeletePoliciesModalProps {
- isLoading: boolean;
- onCancel: () => void;
- onSubmit: () => void;
-}
-
-const DeletePoliciesModal = ({
- isLoading,
- onCancel,
- onSubmit,
-}: IDeletePoliciesModalProps): JSX.Element => {
- return (
-
- <>
- {isLoading ? (
-
- ) : (
-
- Are you sure you want to delete the selected policies?
-
-
- Delete
-
-
- Cancel
-
-
-
- )}
- >
-
- );
-};
-
-export default DeletePoliciesModal;
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/_styles.scss
deleted file mode 100644
index 3cefbc0f8f..0000000000
--- a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/_styles.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.delete-policies-modal {
- font-size: $x-small;
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin-top: $pad-xxlarge;
- }
-
- &__btn {
- margin-left: 12px;
- }
-}
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/index.ts b/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/index.ts
deleted file mode 100644
index 71188244f5..0000000000
--- a/frontend/pages/policies/ManagePoliciesPage/components/DeletePoliciesModal/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from "./DeletePoliciesModal";
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/DeletePolicyModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/DeletePolicyModal.tsx
new file mode 100644
index 0000000000..bc6f8c568e
--- /dev/null
+++ b/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/DeletePolicyModal.tsx
@@ -0,0 +1,48 @@
+import React from "react";
+
+import Modal from "components/Modal";
+import Button from "components/buttons/Button";
+import Spinner from "components/Spinner";
+
+const baseClass = "delete-policy-modal";
+
+interface IDeletePolicyModalProps {
+ isUpdatingPolicies: boolean;
+ onCancel: () => void;
+ onSubmit: () => void;
+}
+
+const DeletePolicyModal = ({
+ isUpdatingPolicies,
+ onCancel,
+ onSubmit,
+}: IDeletePolicyModalProps): JSX.Element => {
+ return (
+
+
+ Are you sure you want to delete the selected policies?
+
+
+ Delete
+
+
+ Cancel
+
+
+
+
+ );
+};
+
+export default DeletePolicyModal;
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/index.ts b/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/index.ts
new file mode 100644
index 0000000000..83b12fbb30
--- /dev/null
+++ b/frontend/pages/policies/ManagePoliciesPage/components/DeletePolicyModal/index.ts
@@ -0,0 +1 @@
+export { default } from "./DeletePolicyModal";
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx
index 54f68ca48b..1ca83e23c2 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/ManageAutomationsModal/ManageAutomationsModal.tsx
@@ -27,7 +27,7 @@ interface IManageAutomationsModalProps {
automationsConfig: IAutomationsConfig | ITeamAutomationsConfig;
availableIntegrations: IIntegrations;
availablePolicies: IPolicy[];
- isAutomationsLoading: boolean;
+ isUpdatingAutomations: boolean;
showPreviewPayloadModal: boolean;
onExit: () => void;
handleSubmit: (formData: IAutomationsConfig | ITeamAutomationsConfig) => void;
@@ -86,8 +86,8 @@ const ManageAutomationsModal = ({
automationsConfig,
availableIntegrations,
availablePolicies,
- isAutomationsLoading,
- showPreviewPayloadModal: showPreviewModal,
+ isUpdatingAutomations,
+ showPreviewPayloadModal,
onExit,
handleSubmit,
togglePreviewPayloadModal: togglePreviewModal,
@@ -337,33 +337,29 @@ const ManageAutomationsModal = ({
);
- return showPreviewModal ? (
+ return showPreviewPayloadModal ? (
renderPreview()
) : (
- <>
- {isAutomationsLoading ? (
-
- ) : (
-
-
- {
- setIsPolicyAutomationsEnabled(!isPolicyAutomationsEnabled);
- setErrors({});
- }}
- inactiveText={"Policy automations disabled"}
- activeText={"Policy automations enabled"}
- />
-
-
-
-
- {availablePolicies?.length ? (
-
-
- {/* {errors.policyItems ? (
+
+
+ {
+ setIsPolicyAutomationsEnabled(!isPolicyAutomationsEnabled);
+ setErrors({});
+ }}
+ inactiveText={"Policy automations disabled"}
+ activeText={"Policy automations enabled"}
+ />
+
+
+
+
+ {availablePolicies?.length ? (
+
+
+ {/* {errors.policyItems ? (
{errors.policyItems}
@@ -372,86 +368,79 @@ const ManageAutomationsModal = ({
Choose which policies you would like to listen to:
)} */}
-
- Choose which policies you would like to listen to:
-
-
- {policyItems &&
- policyItems.map((policyItem) => {
- const { isChecked, name, id } = policyItem;
- return (
-
- {
- updatePolicyItems(policyItem.id);
- !isChecked &&
- setErrors((errs) =>
- omit(errs, "policyItems")
- );
- }}
- >
- {name}
-
-
- );
- })}
-
- ) : (
-
-
You have no policies.
-
Add a policy to turn on automations.
-
- )}
+
+ Choose which policies you would like to listen to:
+
+
+ {policyItems &&
+ policyItems.map((policyItem) => {
+ const { isChecked, name, id } = policyItem;
+ return (
+
+ {
+ updatePolicyItems(policyItem.id);
+ !isChecked &&
+ setErrors((errs) => omit(errs, "policyItems"));
+ }}
+ >
+ {name}
+
+
+ );
+ })}
-
- Workflow
-
-
+ ) : (
+
+
You have no policies.
+
Add a policy to turn on automations.
- {isWebhookEnabled ? renderWebhook() : renderIntegrations()}
-
- {!isPolicyAutomationsEnabled && (
-
)}
-
-
- Cancel
-
-
- Save
-
+
+ Workflow
+
+
+ {isWebhookEnabled ? renderWebhook() : renderIntegrations()}
- )}
- >
+ {!isPolicyAutomationsEnabled && (
+
+ )}
+
+
+
+ Save
+
+
+ Cancel
+
+
+
);
};
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx
index 672cb78574..5a24f19ac9 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTable.tsx
@@ -25,7 +25,7 @@ interface IPoliciesTableProps {
policiesList: IPolicyStats[];
isLoading: boolean;
onAddPolicyClick?: () => void;
- onDeletePoliciesClick: (selectedTableIds: number[]) => void;
+ onDeletePolicyClick: (selectedTableIds: number[]) => void;
canAddOrDeletePolicy?: boolean;
tableType?: string;
currentTeam: ITeamSummary | undefined;
@@ -36,7 +36,7 @@ const PoliciesTable = ({
policiesList,
isLoading,
onAddPolicyClick,
- onDeletePoliciesClick,
+ onDeletePolicyClick,
canAddOrDeletePolicy,
tableType,
currentTeam,
@@ -131,7 +131,7 @@ const PoliciesTable = ({
manualSortBy
showMarkAllPages={false}
isAllPagesSelected={false}
- onPrimarySelectActionClick={onDeletePoliciesClick}
+ onPrimarySelectActionClick={onDeletePolicyClick}
primarySelectActionButtonVariant="text-icon"
primarySelectActionButtonIcon="delete"
primarySelectActionButtonText={"Delete"}
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss
index 52969b1f1d..d3d2ec49ba 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/_styles.scss
@@ -31,6 +31,11 @@
font-size: $x-small;
color: $core-fleet-black;
}
+
+ &__action-button-container {
+ display: flex;
+ justify-content: center;
+ }
}
.has-not-run {
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/PreviewPayloadModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/PreviewPayloadModal.tsx
index 1cbbdca2c4..14c3e0b29b 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/PreviewPayloadModal.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/PreviewPayloadModal.tsx
@@ -63,12 +63,8 @@ const PreviewPayloadModal = ({
-
-
+
+
Done
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/_styles.scss
index 77fee85287..1a81c10993 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PreviewPayloadModal/_styles.scss
@@ -23,15 +23,6 @@
font-weight: $bold;
}
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
-
&__advanced-options-button {
margin: $pad-medium 0;
color: $core-vibrant-blue;
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/PreviewTicketModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/PreviewTicketModal.tsx
index a5fbba7346..02cd840494 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/PreviewTicketModal.tsx
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/PreviewTicketModal.tsx
@@ -40,12 +40,8 @@ const PreviewTicketModal = ({
src={type === "zendesk" ? ZendeskTicket : JiraTicket}
/>
-
-
+
+
Done
diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/_styles.scss
index 6130ec3f25..536b47b622 100644
--- a/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/_styles.scss
+++ b/frontend/pages/policies/ManagePoliciesPage/components/PreviewTicketModal/_styles.scss
@@ -22,13 +22,4 @@
&__info-header {
font-weight: $bold;
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
}
diff --git a/frontend/pages/policies/PolicyPage/components/NewPolicyModal/NewPolicyModal.tsx b/frontend/pages/policies/PolicyPage/components/NewPolicyModal/NewPolicyModal.tsx
index b434716dc6..ea565eb57a 100644
--- a/frontend/pages/policies/PolicyPage/components/NewPolicyModal/NewPolicyModal.tsx
+++ b/frontend/pages/policies/PolicyPage/components/NewPolicyModal/NewPolicyModal.tsx
@@ -21,7 +21,7 @@ export interface INewPolicyModalProps {
setIsNewPolicyModalOpen: (isOpen: boolean) => void;
backendValidators: { [key: string]: string };
platformSelector: IPlatformSelector;
- policyIsLoading: boolean;
+ isUpdatingPolicy: boolean;
}
const validatePolicyName = (name: string) => {
@@ -42,7 +42,7 @@ const NewPolicyModal = ({
setIsNewPolicyModalOpen,
backendValidators,
platformSelector,
- policyIsLoading,
+ isUpdatingPolicy,
}: INewPolicyModalProps): JSX.Element => {
const {
lastEditedQueryName,
@@ -134,16 +134,7 @@ const NewPolicyModal = ({
placeholder="What steps should a device owner take to resolve a host that fails this policy? (optional)"
/>
{platformSelector.render()}
-
-
setIsNewPolicyModalOpen(false)}
- variant="inverse"
- >
- Cancel
-
+
- {policyIsLoading ? : "Save policy"}
+ Save policy
+ setIsNewPolicyModalOpen(false)}
+ variant="inverse"
+ >
+ Cancel
+
>
diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
index 5f4de5a618..2480a51226 100644
--- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
+++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx
@@ -36,7 +36,6 @@ interface IPolicyFormProps {
showOpenSchemaActionText: boolean;
storedPolicy: IPolicy | undefined;
isStoredPolicyLoading: boolean;
- isCreatingNewPolicy: boolean;
isUpdatingPolicy: boolean;
onCreatePolicy: (formData: IPolicyFormData) => void;
onOsqueryTableSelect: (tableName: string) => void;
@@ -64,7 +63,6 @@ const PolicyForm = ({
showOpenSchemaActionText,
storedPolicy,
isStoredPolicyLoading,
- isCreatingNewPolicy,
isUpdatingPolicy,
onCreatePolicy,
onOsqueryTableSelect,
@@ -484,14 +482,35 @@ const PolicyForm = ({
>
{hasSavePermissions && (
- <>{isUpdatingPolicy ? : "Save"}>
+ Save
)}
+
+
+ Select the platform(s) this
+
+ policy will be checked on
+
+ to save or run the policy.
+
+
)}
>
diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss b/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss
index c572bfc502..9c6e223260 100644
--- a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss
+++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss
@@ -173,42 +173,23 @@
}
&__button-wrap {
- display: flex;
- align-items: center;
margin: 0;
-
- &--modal {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- }
+ margin-top: $pad-large;
+ display: flex;
+ gap: $pad-medium;
&--tooltip {
display: flex;
}
- &--modal-save {
- margin-left: $pad-medium;
- }
-
- .policy-form__save {
- margin-right: $pad-small;
- }
.policy-form__run {
min-width: 64px;
- margin: 0;
}
.__react_component_tooltip {
text-align: center;
}
}
- &__button-wrap--modal {
- padding-top: 30px;
- border-top: 1px solid $ui-fleet-blue-15;
- margin-top: 6px;
- }
-
&__title {
color: $core-fleet-black;
display: inline-block;
@@ -228,19 +209,6 @@
}
}
- &__button-wrap--modal-save {
- display: flex;
- }
-
- &__button--modal-save {
- width: 106px;
- }
-
- &__save {
- display: flex;
- width: 64px;
- }
-
&__platform-error {
font-size: $small;
font-weight: $bold;
diff --git a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx b/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx
index 47c775cdcd..db28457083 100644
--- a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx
+++ b/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx
@@ -67,9 +67,6 @@ const QueryEditor = ({
}
}, []);
- const [isCreatingNewPolicy, setIsCreatingNewPolicy] = useState(
- false
- );
const [isUpdatingPolicy, setIsUpdatingPolicy] = useState(false);
const [backendValidators, setBackendValidators] = useState<{
[key: string]: string;
@@ -79,12 +76,12 @@ const QueryEditor = ({
if (policyTeamId) {
formData.team_id = policyTeamId;
}
- setIsCreatingNewPolicy(true);
+ setIsUpdatingPolicy(true);
try {
const policy: IPolicy = await createPolicy(formData).then(
(data) => data.policy
);
- setIsCreatingNewPolicy(false);
+ setIsUpdatingPolicy(false);
router.push(PATHS.EDIT_POLICY(policy));
renderFlash("success", "Policy created!");
} catch (createError: any) {
@@ -100,7 +97,7 @@ const QueryEditor = ({
);
}
} finally {
- setIsCreatingNewPolicy(false);
+ setIsUpdatingPolicy(false);
}
});
@@ -174,7 +171,6 @@ const QueryEditor = ({
storedPolicy={storedPolicy}
policyIdForEdit={policyIdForEdit}
isStoredPolicyLoading={isStoredPolicyLoading}
- isCreatingNewPolicy={isCreatingNewPolicy}
showOpenSchemaActionText={showOpenSchemaActionText}
onOpenSchemaSidebar={onOpenSchemaSidebar}
renderLiveQueryWarning={renderLiveQueryWarning}
diff --git a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
index aaf2189a84..a156f5ef2a 100644
--- a/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
@@ -100,7 +100,7 @@ const ManageQueriesPage = ({
const [showDeleteQueryModal, setShowDeleteQueryModal] = useState(
false
);
- const [queryIsDeleting, setQueryIsDeleting] = useState(false);
+ const [isUpdatingQueries, setIsUpdatingQueries] = useState(false);
const {
data: fleetQueries,
@@ -145,7 +145,7 @@ const ManageQueriesPage = ({
const onDeleteQuerySubmit = useCallback(async () => {
const queryOrQueries = selectedQueryIds.length === 1 ? "query" : "queries";
- setQueryIsDeleting(true);
+ setIsUpdatingQueries(true);
const deleteQueries = selectedQueryIds.map((id) =>
fleetQueriesAPI.destroy(id)
@@ -165,7 +165,7 @@ const ManageQueriesPage = ({
);
} finally {
toggleDeleteQueryModal();
- setQueryIsDeleting(false);
+ setIsUpdatingQueries(false);
}
}, [refetchFleetQueries, selectedQueryIds, toggleDeleteQueryModal]);
@@ -227,7 +227,7 @@ const ManageQueriesPage = ({
{showDeleteQueryModal && (
diff --git a/frontend/pages/queries/ManageQueriesPage/_styles.scss b/frontend/pages/queries/ManageQueriesPage/_styles.scss
index 3526bdd33d..aec551a5cc 100644
--- a/frontend/pages/queries/ManageQueriesPage/_styles.scss
+++ b/frontend/pages/queries/ManageQueriesPage/_styles.scss
@@ -67,16 +67,6 @@
}
}
- &__modal-buttons {
- width: 100%;
- display: flex;
- justify-content: flex-end;
-
- .button:first-child {
- margin-right: $pad-medium;
- }
- }
-
.form-field--dropdown {
margin: 0;
}
@@ -262,7 +252,11 @@
}
&__inner-text {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
width: 485px;
+
img {
width: 12px;
position: relative;
diff --git a/frontend/pages/queries/ManageQueriesPage/components/DeleteQueryModal/DeleteQueryModal.tsx b/frontend/pages/queries/ManageQueriesPage/components/DeleteQueryModal/DeleteQueryModal.tsx
index f0e98b3f24..e2a2525e6c 100644
--- a/frontend/pages/queries/ManageQueriesPage/components/DeleteQueryModal/DeleteQueryModal.tsx
+++ b/frontend/pages/queries/ManageQueriesPage/components/DeleteQueryModal/DeleteQueryModal.tsx
@@ -7,13 +7,13 @@ import Spinner from "components/Spinner";
const baseClass = "delete-query-modal";
interface IDeleteQueryModalProps {
- isLoading: boolean;
+ isUpdatingQueries: boolean;
onCancel: () => void;
onSubmit: () => void;
}
const DeleteQueryModal = ({
- isLoading,
+ isUpdatingQueries,
onCancel,
onSubmit,
}: IDeleteQueryModalProps): JSX.Element => {
@@ -24,23 +24,23 @@ const DeleteQueryModal = ({
onEnter={onSubmit}
className={baseClass}
>
- <>
- {isLoading ? (
-
- ) : (
-
- Are you sure you want to delete the selected queries?
-
-
- Cancel
-
-
- Delete
-
-
-
- )}
- >
+
+ Are you sure you want to delete the selected queries?
+
+
+ Delete
+
+
+ Cancel
+
+
+
);
};
diff --git a/frontend/pages/queries/QueryPage/components/NewQueryModal/NewQueryModal.tsx b/frontend/pages/queries/QueryPage/components/NewQueryModal/NewQueryModal.tsx
index c12431519f..b9349a2e1d 100644
--- a/frontend/pages/queries/QueryPage/components/NewQueryModal/NewQueryModal.tsx
+++ b/frontend/pages/queries/QueryPage/components/NewQueryModal/NewQueryModal.tsx
@@ -114,22 +114,17 @@ const NewQueryModal = ({
where they have access.
-
+
setIsSaveModalOpen(false)}
- variant="inverse"
- >
- Cancel
-
-
- {isLoading ? : "Save query"}
+ Save query
+
+ setIsSaveModalOpen(false)} variant="inverse">
+ Cancel
diff --git a/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx b/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
index 2096ef9ef5..ac1f3d87fc 100644
--- a/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
+++ b/frontend/pages/queries/QueryPage/components/QueryForm/QueryForm.tsx
@@ -488,12 +488,13 @@ const QueryForm = ({
<>
{isEditMode && (
- {isSaveAsNewLoading ? : "Save as new"}
+ Save as new
)}
@@ -508,15 +509,16 @@ const QueryForm = ({
}
>
- {isQueryUpdating ? : "Save"}
+ Save
{" "}
{
wrapperClassName={`${baseClass}__text-editor-wrapper`}
readOnly
/>
-
-
+
+
Done
diff --git a/frontend/pages/queries/QueryPage/components/QueryResults/ShowQueryModal/_styles.scss b/frontend/pages/queries/QueryPage/components/QueryResults/ShowQueryModal/_styles.scss
index 1c737fcb16..7c87bbae15 100644
--- a/frontend/pages/queries/QueryPage/components/QueryResults/ShowQueryModal/_styles.scss
+++ b/frontend/pages/queries/QueryPage/components/QueryResults/ShowQueryModal/_styles.scss
@@ -1,14 +1,4 @@
.show-query-modal {
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- margin: $pad-large 0 0;
- }
-
- &__btn {
- margin-left: 12px;
- }
-
.yaml-ace {
min-height: 0;
}
diff --git a/frontend/pages/schedule/ManageSchedulePage/ManageSchedulePage.tsx b/frontend/pages/schedule/ManageSchedulePage/ManageSchedulePage.tsx
index 950fd58537..048bdc05e5 100644
--- a/frontend/pages/schedule/ManageSchedulePage/ManageSchedulePage.tsx
+++ b/frontend/pages/schedule/ManageSchedulePage/ManageSchedulePage.tsx
@@ -291,8 +291,10 @@ const ManageSchedulePage = ({
const selectedTeamData =
teams?.find((team: ITeam) => selectedTeam === team.id) || undefined;
- const [scheduleIsLoading, setScheduleIsLoading] = useState(false);
- const [scheduleIsRemoving, setScheduleIsRemoving] = useState(false);
+ const [
+ isUpdatingScheduledQuery,
+ setIsUpdatingScheduledQuery,
+ ] = useState(false);
const [showInheritedQueries, setShowInheritedQueries] = useState(
false
);
@@ -342,7 +344,7 @@ const ManageSchedulePage = ({
};
const onRemoveScheduledQuerySubmit = useCallback(() => {
- setScheduleIsRemoving(true);
+ setIsUpdatingScheduledQuery(true);
const promises = selectedQueryIds.map((id: number) => {
return selectedTeamId
? teamScheduledQueriesAPI.destroy(selectedTeamId, id)
@@ -367,7 +369,7 @@ const ManageSchedulePage = ({
})
.finally(() => {
refetchGlobalScheduledQueries();
- setScheduleIsRemoving(false);
+ setIsUpdatingScheduledQuery(false);
});
}, [
selectedTeamId,
@@ -378,7 +380,7 @@ const ManageSchedulePage = ({
const onAddScheduledQuerySubmit = useCallback(
(formData: IFormData, editQuery: IEditScheduledQuery | undefined) => {
- setScheduleIsLoading(true);
+ setIsUpdatingScheduledQuery(true);
if (editQuery) {
const updatedAttributes = deepDifference(formData, editQuery);
@@ -403,7 +405,7 @@ const ManageSchedulePage = ({
);
})
.finally(() => {
- setScheduleIsLoading(false);
+ setIsUpdatingScheduledQuery(false);
refetchGlobalScheduledQueries();
});
} else {
@@ -424,7 +426,7 @@ const ManageSchedulePage = ({
renderFlash("error", "Could not schedule query. Please try again.");
})
.finally(() => {
- setScheduleIsLoading(false);
+ setIsUpdatingScheduledQuery(false);
refetchGlobalScheduledQueries();
});
}
@@ -554,14 +556,14 @@ const ManageSchedulePage = ({
teamId={selectedTeamId}
togglePreviewDataModal={togglePreviewDataModal}
showPreviewDataModal={showPreviewDataModal}
- isLoading={scheduleIsLoading}
+ isUpdatingScheduledQuery={isUpdatingScheduledQuery}
/>
)}
{showRemoveScheduledQueryModal && (
)}
diff --git a/frontend/pages/schedule/ManageSchedulePage/_styles.scss b/frontend/pages/schedule/ManageSchedulePage/_styles.scss
index 76e943cceb..b031bab4e8 100644
--- a/frontend/pages/schedule/ManageSchedulePage/_styles.scss
+++ b/frontend/pages/schedule/ManageSchedulePage/_styles.scss
@@ -72,16 +72,6 @@
margin-right: $pad-medium;
}
- &__modal-buttons {
- width: 100%;
- display: flex;
- justify-content: flex-end;
-
- .button:first-child {
- margin-right: $pad-medium;
- }
- }
-
.Select.is-open {
.Select-value-label {
color: $core-vibrant-blue !important;
@@ -231,6 +221,11 @@
margin: 0;
}
}
+
+ &__cta-buttons {
+ display: flex;
+ justify-content: center;
+ }
}
.no-team-schedule {
diff --git a/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/PreviewDataModal.tsx b/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/PreviewDataModal.tsx
index e224aa1465..3156c73288 100644
--- a/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/PreviewDataModal.tsx
+++ b/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/PreviewDataModal.tsx
@@ -48,12 +48,8 @@ const PreviewDataModal = ({
-
-
+
+
Done
diff --git a/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/_styles.scss b/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/_styles.scss
index b1409b90a6..3e35092aa5 100644
--- a/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/_styles.scss
+++ b/frontend/pages/schedule/ManageSchedulePage/components/PreviewDataModal/_styles.scss
@@ -18,13 +18,4 @@
font-size: $x-small;
text-decoration: none;
}
-
- &__btn-wrap {
- display: flex;
- flex-direction: row-reverse;
- }
-
- &__btn {
- margin-left: 12px;
- }
}
diff --git a/frontend/pages/schedule/ManageSchedulePage/components/RemoveScheduledQueryModal/RemoveScheduledQueryModal.tsx b/frontend/pages/schedule/ManageSchedulePage/components/RemoveScheduledQueryModal/RemoveScheduledQueryModal.tsx
index 1e10813892..711f9b58eb 100644
--- a/frontend/pages/schedule/ManageSchedulePage/components/RemoveScheduledQueryModal/RemoveScheduledQueryModal.tsx
+++ b/frontend/pages/schedule/ManageSchedulePage/components/RemoveScheduledQueryModal/RemoveScheduledQueryModal.tsx
@@ -7,13 +7,13 @@ import Spinner from "components/Spinner";
const baseClass = "remove-scheduled-query-modal";
interface IRemoveScheduledQueryModalProps {
- isLoading: boolean;
+ isUpdatingScheduledQuery: boolean;
onCancel: () => void;
onSubmit: () => void;
}
const RemoveScheduledQueryModal = ({
- isLoading,
+ isUpdatingScheduledQuery,
onCancel,
onSubmit,
}: IRemoveScheduledQueryModalProps): JSX.Element => {
@@ -24,22 +24,23 @@ const RemoveScheduledQueryModal = ({
onEnter={onSubmit}
className={baseClass}
>
- {isLoading ? (
-
- ) : (
-
- Are you sure you want to remove the selected queries from the
- schedule?
-
-
- Cancel
-
-
- Remove
-
-
+
+ Are you sure you want to remove the selected queries from the schedule?
+
+
+ Remove
+
+
+ Cancel
+
- )}
+
);
};
diff --git a/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx b/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx
index 0b04c8cec4..5d3e1d186d 100644
--- a/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx
+++ b/frontend/pages/schedule/ManageSchedulePage/components/ScheduleEditorModal/ScheduleEditorModal.tsx
@@ -50,7 +50,7 @@ interface IScheduleEditorModalProps {
teamId?: number;
togglePreviewDataModal: () => void;
showPreviewDataModal: boolean;
- isLoading: boolean;
+ isUpdatingScheduledQuery: boolean;
}
interface INoQueryOption {
id: number;
@@ -94,7 +94,7 @@ const ScheduleEditorModal = ({
teamId,
togglePreviewDataModal,
showPreviewDataModal,
- isLoading,
+ isUpdatingScheduledQuery,
}: IScheduleEditorModalProps): JSX.Element => {
const { config } = useContext(AppContext);
@@ -234,124 +234,122 @@ const ScheduleEditorModal = ({
onEnter={onFormSubmit}
className={baseClass}
>
- {isLoading ? (
-
- ) : (
-