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:
RachelElysia 2021-09-23 13:10:43 -04:00 committed by GitHub
parent fc00940660
commit 7dee7c56ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 356 additions and 93 deletions

View file

@ -0,0 +1 @@
* Fleet premium teams schedules surfaces inherited queries from All teams (global) schedule

View file

@ -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");
}); });
} }
); );

View file

@ -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()

View file

@ -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");
}); });
*/
}); });

View file

@ -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";

View file

@ -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 teams hosts.</p></center>\
"
}
/>
</div>
</>
) : null}
{showInheritedQueries &&
renderAllTeamsTable(
teamId,
allTeamsScheduledQueriesList,
allTeamsScheduledQueriesError
)}
{showScheduleEditorModal && ( {showScheduleEditorModal && (
<ScheduleEditorModal <ScheduleEditorModal
onCancel={toggleScheduleEditorModal} onCancel={toggleScheduleEditorModal}

View file

@ -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;
}
}
} }

View file

@ -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

View file

@ -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,
};