mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Schedules Page: Surface all teams schedule on team schedule page (#2080)
* Render inherited queries table on teams page * e2e team schedules on e2e premium testing
This commit is contained in:
parent
fc00940660
commit
7dee7c56ef
9 changed files with 356 additions and 93 deletions
1
changes/issue-1894-surface-inherited-queries
Normal file
1
changes/issue-1894-surface-inherited-queries
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* Fleet premium teams schedules surfaces inherited queries from All teams (global) schedule
|
||||||
|
|
@ -9,50 +9,43 @@ describe(
|
||||||
cy.login();
|
cy.login();
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - Fix tests according to improved query experience - MP
|
|
||||||
it("Create, check, edit, and delete a query successfully and create, edit, and delete a global scheduled query successfully", () => {
|
it("Create, check, edit, and delete a query successfully and create, edit, and delete a global scheduled query successfully", () => {
|
||||||
cy.visit("/queries/manage");
|
cy.visit("/queries/manage");
|
||||||
|
|
||||||
cy.findByRole("button", { name: /create new query/i }).should("exist");
|
cy.findByRole("button", { name: /create new query/i }).click();
|
||||||
// cy.findByRole("button", { name: /create new query/i }).click();
|
|
||||||
|
|
||||||
// cy.findByLabelText(/query name/i)
|
// Using class selector because third party element doesn't work with Cypress Testing Selector Library
|
||||||
// .click()
|
cy.get(".ace_scroller")
|
||||||
// .type("Query all window crashes");
|
.click({ force: true })
|
||||||
|
.type("{selectall}SELECT * FROM windows_crashes;");
|
||||||
|
|
||||||
// // Using class selector because third party element doesn't work with Cypress Testing Selector Library
|
cy.findByRole("button", { name: /save/i }).click();
|
||||||
// cy.get(".ace_scroller")
|
|
||||||
// .click({ force: true })
|
|
||||||
// .type("{selectall}{backspace}SELECT * FROM windows_crashes;");
|
|
||||||
|
|
||||||
// cy.findByLabelText(/description/i)
|
cy.findByLabelText(/name/i).click().type("Query all window crashes");
|
||||||
// .click()
|
|
||||||
// .type("See all window crashes");
|
|
||||||
|
|
||||||
// cy.findByRole("button", { name: /save/i }).click();
|
cy.findByLabelText(/description/i)
|
||||||
|
.click()
|
||||||
|
.type("See all window crashes");
|
||||||
|
|
||||||
// cy.findByRole("button", { name: /save as new/i }).click();
|
cy.findByRole("button", { name: /save query/i }).click();
|
||||||
|
|
||||||
// Just refreshes to create new query, needs success alert to user that they created a query
|
cy.findByText(/query created/i).should("exist");
|
||||||
// cy.visit("/queries/manage");
|
cy.findByText(/back to queries/i).should("exist");
|
||||||
|
cy.visit("/queries/manage");
|
||||||
|
|
||||||
// cy.findByText(/query all/i).click();
|
cy.findByText(/query all/i).click();
|
||||||
|
|
||||||
// cy.findByText(/edit & run query/i).should("exist");
|
cy.findByText(/run query/i).should("exist");
|
||||||
|
|
||||||
// cy.get(".ace_scroller")
|
cy.get(".ace_scroller")
|
||||||
// .click({ force: true })
|
.click({ force: true })
|
||||||
// .type(
|
.type("{selectall}SELECT datetime, username FROM windows_crashes;");
|
||||||
// "{selectall}{backspace}SELECT datetime, username FROM windows_crashes;"
|
|
||||||
// );
|
|
||||||
|
|
||||||
// cy.findByRole("button", { name: /save/i }).click();
|
cy.findByRole("button", { name: /^Save$/ }).click();
|
||||||
|
|
||||||
// cy.findByRole("button", { name: /save changes/i }).click();
|
cy.findByText(/query updated/i).should("be.visible");
|
||||||
|
|
||||||
// cy.findByText(/query updated/i).should("be.visible");
|
// // Start e2e test for schedules
|
||||||
|
|
||||||
// // Test Schedules
|
|
||||||
// cy.visit("/schedule/manage");
|
// cy.visit("/schedule/manage");
|
||||||
|
|
||||||
// cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
// cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
|
@ -85,64 +78,67 @@ describe(
|
||||||
// .contains("button", /schedule/i)
|
// .contains("button", /schedule/i)
|
||||||
// .click();
|
// .click();
|
||||||
|
|
||||||
// cy.visit("/schedule/manage");
|
// cy.visit("/schedule/manage");
|
||||||
|
|
||||||
// cy.wait(3000); // eslint-disable-line cypress/no-unnecessary-waiting
|
// cy.wait(2000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
// cy.findByText(/query all window crashes/i).should("exist");
|
// cy.findByText(/query all window crashes/i).should("exist");
|
||||||
|
|
||||||
// cy.findByText(/actions/i).click();
|
// cy.findByText(/actions/i).click();
|
||||||
// cy.findByText(/edit/i).click();
|
// cy.findByText(/edit/i).click();
|
||||||
|
|
||||||
// cy.get(
|
// cy.get(
|
||||||
// ".schedule-editor-modal__form-field--frequency > .dropdown__select"
|
// ".schedule-editor-modal__form-field--frequency > .dropdown__select"
|
||||||
// ).click();
|
// ).click();
|
||||||
|
|
||||||
// cy.findByText(/every 6 hours/i).click();
|
// cy.findByText(/every 6 hours/i).click();
|
||||||
|
|
||||||
// cy.findByText(/show advanced options/i).click();
|
// cy.findByText(/show advanced options/i).click();
|
||||||
|
|
||||||
// cy.findByText(/ignore removals/i).click();
|
// cy.findByText(/ignore removals/i).click();
|
||||||
// cy.findByText(/snapshot/i).click();
|
// cy.findByText(/snapshot/i).click();
|
||||||
|
|
||||||
// cy.get(".schedule-editor-modal__form-field--shard > .input-field")
|
// cy.get(".schedule-editor-modal__form-field--shard > .input-field")
|
||||||
// .click()
|
// .click()
|
||||||
// .type("{selectall}{backspace}10");
|
// .type("{selectall}{backspace}10");
|
||||||
|
|
||||||
// cy.get(".schedule-editor-modal__btn-wrap")
|
// cy.get(".schedule-editor-modal__btn-wrap")
|
||||||
// .contains("button", /schedule/i)
|
// .contains("button", /schedule/i)
|
||||||
// .click();
|
// .click();
|
||||||
|
|
||||||
// cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
// cy.visit("/schedule/manage");
|
||||||
// cy.findByText(/actions/i).click();
|
|
||||||
// cy.findByText(/remove/i).click();
|
|
||||||
|
|
||||||
// cy.get(".remove-scheduled-query-modal__btn-wrap")
|
// cy.wait(2000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
// .contains("button", /remove/i)
|
// cy.findByText(/actions/i).click();
|
||||||
// .click();
|
// cy.findByText(/remove/i).click();
|
||||||
|
|
||||||
// cy.findByText(/query all window crashes/i).should("not.exist");
|
// cy.get(".remove-scheduled-query-modal__btn-wrap")
|
||||||
// // End Test Schedules
|
// .contains("button", /remove/i)
|
||||||
|
// .click();
|
||||||
|
|
||||||
// cy.visit("/queries/manage");
|
// cy.findByText(/query all window crashes/i).should("not.exist");
|
||||||
|
|
||||||
// cy.findByText(/query all window crashes/i)
|
// // End e2e test for schedules
|
||||||
// .parent()
|
|
||||||
// .parent()
|
|
||||||
// .within(() => {
|
|
||||||
// cy.get(".fleet-checkbox__input").check({ force: true });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// cy.findByRole("button", { name: /delete/i }).click();
|
cy.visit("/queries/manage");
|
||||||
|
|
||||||
// // Can't figure out how attach findByRole onto modal button
|
cy.findByText(/query all window crashes/i)
|
||||||
// // Can't use findByText because delete button under modal
|
.parent()
|
||||||
// cy.get(".remove-query-modal")
|
.parent()
|
||||||
// .contains("button", /delete/i)
|
.within(() => {
|
||||||
// .click();
|
cy.get(".fleet-checkbox__input").check({ force: true });
|
||||||
|
});
|
||||||
|
|
||||||
// cy.findByText(/successfully removed query/i).should("be.visible");
|
cy.findByRole("button", { name: /delete/i }).click();
|
||||||
|
|
||||||
// cy.findByText(/query all/i).should("not.exist");
|
// Can't figure out how attach findByRole onto modal button
|
||||||
|
// Can't use findByText because delete button under modal
|
||||||
|
cy.get(".remove-query-modal")
|
||||||
|
.contains("button", /delete/i)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.findByText(/successfully removed query/i).should("be.visible");
|
||||||
|
|
||||||
|
cy.findByText(/query all/i).should("not.exist");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -65,13 +65,11 @@ describe("Free tier - Observer user", () => {
|
||||||
cy.findByText(/show sql/i).click();
|
cy.findByText(/show sql/i).click();
|
||||||
cy.findByRole("button", { name: /run query/i }).should("exist");
|
cy.findByRole("button", { name: /run query/i }).should("exist");
|
||||||
|
|
||||||
cy.visit("/queries/manage");
|
|
||||||
|
|
||||||
cy.findByText(/get authorized/i).should("not.exist");
|
|
||||||
|
|
||||||
// On the Profile page, they should…
|
// On the Profile page, they should…
|
||||||
// See Observer in Role section, and no Team section
|
// See Observer in Role section, and no Team section
|
||||||
cy.visit("/profile");
|
cy.visit("/profile");
|
||||||
|
|
||||||
|
cy.wait(2000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
cy.findByText(/teams/i).should("not.exist");
|
cy.findByText(/teams/i).should("not.exist");
|
||||||
cy.findByText("Role")
|
cy.findByText("Role")
|
||||||
.next()
|
.next()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("Teams flow", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
/* TODO fix and reenable
|
/* TODO fix and reenable
|
||||||
This test is causing major flake issues due to the dropdown menu
|
This test is causing major flake issues due to the dropdown menu */
|
||||||
|
|
||||||
it("Create, edit, and delete a team successfully", () => {
|
it("Create, edit, and delete a team successfully", () => {
|
||||||
cy.visit("/settings/teams");
|
cy.visit("/settings/teams");
|
||||||
|
|
@ -44,13 +44,77 @@ describe("Teams flow", () => {
|
||||||
cy.contains(/config:/i).should("be.visible");
|
cy.contains(/config:/i).should("be.visible");
|
||||||
cy.contains(/options:/i).should("be.visible");
|
cy.contains(/options:/i).should("be.visible");
|
||||||
|
|
||||||
|
// Check team in schedules
|
||||||
|
cy.visit("/queries/manage");
|
||||||
|
|
||||||
|
cy.findByRole("button", { name: /create new query/i }).click();
|
||||||
|
|
||||||
|
// Using class selector because third party element doesn't work with Cypress Testing Selector Library
|
||||||
|
cy.get(".ace_scroller")
|
||||||
|
.click({ force: true })
|
||||||
|
.type("{selectall}SELECT * FROM windows_crashes;");
|
||||||
|
|
||||||
|
cy.findByRole("button", { name: /save/i }).click();
|
||||||
|
|
||||||
|
cy.findByLabelText(/name/i).click().type("Query all window crashes");
|
||||||
|
|
||||||
|
cy.findByLabelText(/description/i)
|
||||||
|
.click()
|
||||||
|
.type("See all window crashes");
|
||||||
|
|
||||||
|
cy.findByRole("button", { name: /save query/i }).click();
|
||||||
|
|
||||||
|
cy.visit("/schedule/manage");
|
||||||
|
|
||||||
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
|
||||||
|
cy.findByRole("button", { name: /schedule a query/i }).click();
|
||||||
|
|
||||||
|
cy.findByText(/select query/i).click();
|
||||||
|
|
||||||
|
cy.findByText(/query all window crashes/i).click();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
".schedule-editor-modal__form-field--frequency > .dropdown__select"
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.findByText(/every week/i).click();
|
||||||
|
|
||||||
|
cy.findByText(/show advanced options/i).click();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
".schedule-editor-modal__form-field--logging > .dropdown__select"
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.findByText(/ignore removals/i).click();
|
||||||
|
|
||||||
|
cy.get(".schedule-editor-modal__form-field--shard > .input-field")
|
||||||
|
.click()
|
||||||
|
.type("50");
|
||||||
|
|
||||||
|
cy.get(".schedule-editor-modal__btn-wrap")
|
||||||
|
.contains("button", /schedule/i)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.visit("/schedule/manage");
|
||||||
|
|
||||||
|
cy.wait(2000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
cy.findByText(/all teams/i).click();
|
||||||
|
cy.findByText(/valor/i).click();
|
||||||
|
|
||||||
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
cy.findByText(/query all window crashes/i).should("not.exist");
|
||||||
|
cy.findByText(/inherited query/i).click();
|
||||||
|
cy.findByText(/query all window crashes/i).should("exist");
|
||||||
|
|
||||||
|
// Edit Team
|
||||||
cy.visit("/settings/teams");
|
cy.visit("/settings/teams");
|
||||||
|
|
||||||
cy.contains("Valor").get(".Select-arrow-zone").click();
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
|
cy.findByText(/actions/i).click({ force: true });
|
||||||
// need force:true for dropdown
|
cy.findByText(/edit/i).click({ force: true }); // need force:true for dropdown
|
||||||
cy.findByText(/edit/i).click({ force: true });
|
|
||||||
|
|
||||||
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
cy.findByLabelText(/team name/i)
|
cy.findByLabelText(/team name/i)
|
||||||
.click()
|
.click()
|
||||||
.type("{selectall}{backspace}Mystic");
|
.type("{selectall}{backspace}Mystic");
|
||||||
|
|
@ -66,6 +130,7 @@ describe("Teams flow", () => {
|
||||||
|
|
||||||
cy.findByText(/delete/i).click({ force: true });
|
cy.findByText(/delete/i).click({ force: true });
|
||||||
|
|
||||||
|
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
|
||||||
cy.findByRole("button", { name: /delete/i }).click();
|
cy.findByRole("button", { name: /delete/i }).click();
|
||||||
|
|
||||||
cy.findByText(/successfully deleted/i).should("be.visible");
|
cy.findByText(/successfully deleted/i).should("be.visible");
|
||||||
|
|
@ -77,5 +142,4 @@ describe("Teams flow", () => {
|
||||||
|
|
||||||
cy.findByText(/mystic/i).should("not.exist");
|
cy.findByText(/mystic/i).should("not.exist");
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import enrollSecretInterface from "interfaces/enroll_secret";
|
||||||
import EnrollSecretTable from "components/config/EnrollSecretTable";
|
import EnrollSecretTable from "components/config/EnrollSecretTable";
|
||||||
import InputField from "components/forms/fields/InputField";
|
import InputField from "components/forms/fields/InputField";
|
||||||
import OrgLogoIcon from "components/icons/OrgLogoIcon";
|
import OrgLogoIcon from "components/icons/OrgLogoIcon";
|
||||||
import Slider from "components/forms/fields/Slider";
|
|
||||||
import validate from "components/forms/admin/AppConfigForm/validate";
|
import validate from "components/forms/admin/AppConfigForm/validate";
|
||||||
import IconToolTip from "components/IconToolTip";
|
import IconToolTip from "components/IconToolTip";
|
||||||
import InfoBanner from "components/InfoBanner/InfoBanner";
|
import InfoBanner from "components/InfoBanner/InfoBanner";
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import paths from "router/paths";
|
||||||
import Button from "components/buttons/Button";
|
import Button from "components/buttons/Button";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Dropdown from "components/forms/fields/Dropdown";
|
import Dropdown from "components/forms/fields/Dropdown";
|
||||||
|
import IconToolTip from "components/IconToolTip";
|
||||||
import TableDataError from "components/TableDataError";
|
import TableDataError from "components/TableDataError";
|
||||||
import ScheduleListWrapper from "./components/ScheduleListWrapper";
|
import ScheduleListWrapper from "./components/ScheduleListWrapper";
|
||||||
import ScheduleEditorModal from "./components/ScheduleEditorModal";
|
import ScheduleEditorModal from "./components/ScheduleEditorModal";
|
||||||
|
|
@ -33,8 +34,10 @@ import RemoveScheduledQueryModal from "./components/RemoveScheduledQueryModal";
|
||||||
const baseClass = "manage-schedule-page";
|
const baseClass = "manage-schedule-page";
|
||||||
|
|
||||||
const renderTable = (
|
const renderTable = (
|
||||||
onRemoveScheduledQueryClick: React.MouseEventHandler<HTMLButtonElement>,
|
onRemoveScheduledQueryClick: (selectIds: number[]) => void,
|
||||||
onEditScheduledQueryClick: React.MouseEventHandler<HTMLButtonElement>,
|
onEditScheduledQueryClick: (
|
||||||
|
selectedQuery: IGlobalScheduledQuery | ITeamScheduledQuery
|
||||||
|
) => void,
|
||||||
allScheduledQueriesList: IGlobalScheduledQuery[] | ITeamScheduledQuery[],
|
allScheduledQueriesList: IGlobalScheduledQuery[] | ITeamScheduledQuery[],
|
||||||
allScheduledQueriesError: { name: string; reason: string }[],
|
allScheduledQueriesError: { name: string; reason: string }[],
|
||||||
toggleScheduleEditorModal: () => void,
|
toggleScheduleEditorModal: () => void,
|
||||||
|
|
@ -55,6 +58,26 @@ const renderTable = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderAllTeamsTable = (
|
||||||
|
teamId: number,
|
||||||
|
allTeamsScheduledQueriesList: IGlobalScheduledQuery[],
|
||||||
|
allTeamsScheduledQueriesError: { name: string; reason: string }[]
|
||||||
|
): JSX.Element => {
|
||||||
|
if (Object.keys(allTeamsScheduledQueriesError).length > 0) {
|
||||||
|
return <TableDataError />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${baseClass}__all-teams-table`}>
|
||||||
|
<ScheduleListWrapper
|
||||||
|
inheritedQueries
|
||||||
|
allScheduledQueriesList={allTeamsScheduledQueriesList}
|
||||||
|
teamId={teamId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
interface ITeamSchedulesPageProps {
|
interface ITeamSchedulesPageProps {
|
||||||
params: {
|
params: {
|
||||||
team_id: string;
|
team_id: string;
|
||||||
|
|
@ -139,6 +162,18 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
const allScheduledQueriesList = Object.values(allScheduledQueries.data);
|
const allScheduledQueriesList = Object.values(allScheduledQueries.data);
|
||||||
const allScheduledQueriesError = allScheduledQueries.errors;
|
const allScheduledQueriesError = allScheduledQueries.errors;
|
||||||
|
|
||||||
|
const allTeamsScheduledQueries = useSelector((state: IRootState) => {
|
||||||
|
return state.entities.global_scheduled_queries;
|
||||||
|
});
|
||||||
|
|
||||||
|
const allTeamsScheduledQueriesList = Object.values(
|
||||||
|
allTeamsScheduledQueries.data
|
||||||
|
);
|
||||||
|
const allTeamsScheduledQueriesError = allTeamsScheduledQueries.errors;
|
||||||
|
|
||||||
|
const inheritedQueryOrQueries =
|
||||||
|
allTeamsScheduledQueriesList.length === 1 ? "query" : "queries";
|
||||||
|
|
||||||
const allTeams = useSelector((state: IRootState) => state.entities.teams);
|
const allTeams = useSelector((state: IRootState) => state.entities.teams);
|
||||||
const allTeamsList = Object.values(allTeams.data);
|
const allTeamsList = Object.values(allTeams.data);
|
||||||
|
|
||||||
|
|
@ -148,7 +183,7 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
const teamOptions: ITeamOptions[] = [
|
const teamOptions: ITeamOptions[] = [
|
||||||
{
|
{
|
||||||
disabled: false,
|
disabled: false,
|
||||||
label: "Global",
|
label: "All teams",
|
||||||
value: "global",
|
value: "global",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -163,6 +198,9 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
return teamOptions;
|
return teamOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [showInheritedQueries, setShowInheritedQueries] = useState<boolean>(
|
||||||
|
false
|
||||||
|
);
|
||||||
const [showScheduleEditorModal, setShowScheduleEditorModal] = useState(false);
|
const [showScheduleEditorModal, setShowScheduleEditorModal] = useState(false);
|
||||||
const [
|
const [
|
||||||
showRemoveScheduledQueryModal,
|
showRemoveScheduledQueryModal,
|
||||||
|
|
@ -175,6 +213,10 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
IGlobalScheduledQuery | ITeamScheduledQuery
|
IGlobalScheduledQuery | ITeamScheduledQuery
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
const toggleInheritedQueries = () => {
|
||||||
|
setShowInheritedQueries(!showInheritedQueries);
|
||||||
|
};
|
||||||
|
|
||||||
const toggleScheduleEditorModal = useCallback(() => {
|
const toggleScheduleEditorModal = useCallback(() => {
|
||||||
setSelectedScheduledQuery(undefined); // create modal renders
|
setSelectedScheduledQuery(undefined); // create modal renders
|
||||||
setShowScheduleEditorModal(!showScheduleEditorModal);
|
setShowScheduleEditorModal(!showScheduleEditorModal);
|
||||||
|
|
@ -184,12 +226,16 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
setShowRemoveScheduledQueryModal(!showRemoveScheduledQueryModal);
|
setShowRemoveScheduledQueryModal(!showRemoveScheduledQueryModal);
|
||||||
}, [showRemoveScheduledQueryModal, setShowRemoveScheduledQueryModal]);
|
}, [showRemoveScheduledQueryModal, setShowRemoveScheduledQueryModal]);
|
||||||
|
|
||||||
const onRemoveScheduledQueryClick = (selectedTableQueryIds: any): void => {
|
const onRemoveScheduledQueryClick = (
|
||||||
|
selectedTableQueryIds: number[]
|
||||||
|
): void => {
|
||||||
toggleRemoveScheduledQueryModal();
|
toggleRemoveScheduledQueryModal();
|
||||||
setSelectedQueryIds(selectedTableQueryIds);
|
setSelectedQueryIds(selectedTableQueryIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEditScheduledQueryClick = (selectedQuery: any): void => {
|
const onEditScheduledQueryClick = (
|
||||||
|
selectedQuery: IGlobalScheduledQuery | ITeamScheduledQuery
|
||||||
|
): void => {
|
||||||
toggleScheduleEditorModal();
|
toggleScheduleEditorModal();
|
||||||
setSelectedScheduledQuery(selectedQuery); // edit modal renders
|
setSelectedScheduledQuery(selectedQuery); // edit modal renders
|
||||||
};
|
};
|
||||||
|
|
@ -379,6 +425,39 @@ const ManageSchedulePage = (props: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
teamId
|
teamId
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* must use ternary for NaN */}
|
||||||
|
{teamId && allTeamsScheduledQueriesList.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
<Button
|
||||||
|
variant="unstyled"
|
||||||
|
className={`${showInheritedQueries ? "upcarat" : "rightcarat"}
|
||||||
|
${baseClass}__inherited-queries-button`}
|
||||||
|
onClick={toggleInheritedQueries}
|
||||||
|
>
|
||||||
|
{showInheritedQueries
|
||||||
|
? `Hide ${allTeamsScheduledQueriesList.length} inherited ${inheritedQueryOrQueries}`
|
||||||
|
: `Show ${allTeamsScheduledQueriesList.length} inherited ${inheritedQueryOrQueries}`}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
<div className={`${baseClass}__details`}>
|
||||||
|
<IconToolTip
|
||||||
|
isHtml
|
||||||
|
text={
|
||||||
|
"\
|
||||||
|
<center><p>Queries from the “All teams”<br/>schedule run on this team’s hosts.</p></center>\
|
||||||
|
"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{showInheritedQueries &&
|
||||||
|
renderAllTeamsTable(
|
||||||
|
teamId,
|
||||||
|
allTeamsScheduledQueriesList,
|
||||||
|
allTeamsScheduledQueriesError
|
||||||
|
)}
|
||||||
{showScheduleEditorModal && (
|
{showScheduleEditorModal && (
|
||||||
<ScheduleEditorModal
|
<ScheduleEditorModal
|
||||||
onCancel={toggleScheduleEditorModal}
|
onCancel={toggleScheduleEditorModal}
|
||||||
|
|
|
||||||
|
|
@ -174,4 +174,64 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__inherited-queries-button {
|
||||||
|
margin: $pad-medium 0 0 0;
|
||||||
|
color: $core-vibrant-blue;
|
||||||
|
font-weight: $bold;
|
||||||
|
font-size: $x-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightcarat {
|
||||||
|
&::before {
|
||||||
|
content: url("../assets/images/icon-chevron-blue-16x16@2x.png");
|
||||||
|
transform: scale(0.5) rotate(-90deg);
|
||||||
|
width: 16px;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upcarat {
|
||||||
|
&::before {
|
||||||
|
content: url("../assets/images/icon-chevron-blue-16x16@2x.png");
|
||||||
|
transform: scale(0.5) rotate(180deg);
|
||||||
|
width: 16px;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
padding-right: 2px;
|
||||||
|
margin-right: $pad-small;
|
||||||
|
margin-top: 5px;
|
||||||
|
position: relative;
|
||||||
|
top: -4px;
|
||||||
|
left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
display: inline-flex;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: $pad-small;
|
||||||
|
margin-top: -20px;
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
color: $core-fleet-black;
|
||||||
|
|
||||||
|
&--brand {
|
||||||
|
color: $core-vibrant-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__all-teams-table {
|
||||||
|
.table-container__header {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-right: 1px solid #e2e4ea !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,11 @@ import { ITeamScheduledQuery } from "interfaces/team_scheduled_query";
|
||||||
import globalScheduledQueryActions from "redux/nodes/entities/global_scheduled_queries/actions";
|
import globalScheduledQueryActions from "redux/nodes/entities/global_scheduled_queries/actions";
|
||||||
|
|
||||||
import TableContainer from "components/TableContainer";
|
import TableContainer from "components/TableContainer";
|
||||||
import { generateTableHeaders, generateDataSet } from "./ScheduleTableConfig";
|
import {
|
||||||
|
generateInheritedQueriesTableHeaders,
|
||||||
|
generateTableHeaders,
|
||||||
|
generateDataSet,
|
||||||
|
} from "./ScheduleTableConfig";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import scheduleSvg from "../../../../../../assets/images/schedule.svg";
|
import scheduleSvg from "../../../../../../assets/images/schedule.svg";
|
||||||
|
|
||||||
|
|
@ -21,11 +25,14 @@ const baseClass = "schedule-list-wrapper";
|
||||||
const noScheduleClass = "no-schedule";
|
const noScheduleClass = "no-schedule";
|
||||||
|
|
||||||
interface IScheduleListWrapperProps {
|
interface IScheduleListWrapperProps {
|
||||||
onRemoveScheduledQueryClick: any;
|
onRemoveScheduledQueryClick?: (selectIds: number[]) => void;
|
||||||
onEditScheduledQueryClick: any;
|
onEditScheduledQueryClick?: (
|
||||||
|
selectedQuery: IGlobalScheduledQuery | ITeamScheduledQuery
|
||||||
|
) => void;
|
||||||
allScheduledQueriesList: IGlobalScheduledQuery[] | ITeamScheduledQuery[];
|
allScheduledQueriesList: IGlobalScheduledQuery[] | ITeamScheduledQuery[];
|
||||||
toggleScheduleEditorModal: () => void;
|
toggleScheduleEditorModal?: () => void;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
|
inheritedQueries?: boolean;
|
||||||
}
|
}
|
||||||
interface IRootState {
|
interface IRootState {
|
||||||
entities: {
|
entities: {
|
||||||
|
|
@ -47,6 +54,7 @@ const ScheduleListWrapper = (props: IScheduleListWrapperProps): JSX.Element => {
|
||||||
toggleScheduleEditorModal,
|
toggleScheduleEditorModal,
|
||||||
onEditScheduledQueryClick,
|
onEditScheduledQueryClick,
|
||||||
teamId,
|
teamId,
|
||||||
|
inheritedQueries,
|
||||||
} = props;
|
} = props;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { MANAGE_PACKS } = paths;
|
const { MANAGE_PACKS } = paths;
|
||||||
|
|
@ -92,10 +100,14 @@ const ScheduleListWrapper = (props: IScheduleListWrapperProps): JSX.Element => {
|
||||||
): void => {
|
): void => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "edit":
|
case "edit":
|
||||||
onEditScheduledQueryClick(global_scheduled_query);
|
if (onEditScheduledQueryClick) {
|
||||||
|
onEditScheduledQueryClick(global_scheduled_query);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
onRemoveScheduledQueryClick([global_scheduled_query.id]);
|
if (onRemoveScheduledQueryClick) {
|
||||||
|
onRemoveScheduledQueryClick([global_scheduled_query.id]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -123,6 +135,33 @@ const ScheduleListWrapper = (props: IScheduleListWrapperProps): JSX.Element => {
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const loadingInheritedQueriesTableData = useSelector((state: IRootState) => {
|
||||||
|
return state.entities.global_scheduled_queries.isLoading;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (inheritedQueries) {
|
||||||
|
const inheritedQueriesTableHeaders = generateInheritedQueriesTableHeaders();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${baseClass}`}>
|
||||||
|
<TableContainer
|
||||||
|
resultsTitle={"queries"}
|
||||||
|
columns={inheritedQueriesTableHeaders}
|
||||||
|
data={generateDataSet(allScheduledQueriesList, teamId)}
|
||||||
|
isLoading={loadingInheritedQueriesTableData}
|
||||||
|
defaultSortHeader={"query"}
|
||||||
|
defaultSortDirection={"desc"}
|
||||||
|
showMarkAllPages={false}
|
||||||
|
isAllPagesSelected={false}
|
||||||
|
searchable={false}
|
||||||
|
disablePagination
|
||||||
|
disableCount
|
||||||
|
emptyComponent={NoScheduledQueries}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${baseClass}`}>
|
<div className={`${baseClass}`}>
|
||||||
<TableContainer
|
<TableContainer
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,29 @@ const generateTableHeaders = (
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateInheritedQueriesTableHeaders = (): IDataColumn[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: "Query",
|
||||||
|
Header: "Query",
|
||||||
|
disableSortBy: true,
|
||||||
|
accessor: "query_name",
|
||||||
|
Cell: (cellProps: ICellProps): JSX.Element => (
|
||||||
|
<TextCell value={cellProps.cell.value} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Frequency",
|
||||||
|
Header: "Frequency",
|
||||||
|
disableSortBy: true,
|
||||||
|
accessor: "interval",
|
||||||
|
Cell: (cellProps: ICellProps): JSX.Element => (
|
||||||
|
<TextCell value={secondsToDhms(cellProps.cell.value)} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
const generateActionDropdownOptions = (): IDropdownOption[] => {
|
const generateActionDropdownOptions = (): IDropdownOption[] => {
|
||||||
const dropdownOptions = [
|
const dropdownOptions = [
|
||||||
{
|
{
|
||||||
|
|
@ -162,4 +185,8 @@ const generateDataSet = (
|
||||||
return [...enhanceAllScheduledQueryData(all_scheduled_queries, teamId)];
|
return [...enhanceAllScheduledQueryData(all_scheduled_queries, teamId)];
|
||||||
};
|
};
|
||||||
|
|
||||||
export { generateTableHeaders, generateDataSet };
|
export {
|
||||||
|
generateInheritedQueriesTableHeaders,
|
||||||
|
generateTableHeaders,
|
||||||
|
generateDataSet,
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue