| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- /*
- Copyright (C) 2020 Cloudbase Solutions SRL
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- import * as React from "react";
- import styled from "styled-components";
- import {
- MinionPoolDetails,
- MinionPoolEventProgressUpdate,
- } from "@src/@types/MinionPool";
- import { ThemePalette, ThemeProps } from "@src/components/Theme";
- import DropdownLink from "@src/components/ui/Dropdowns/DropdownLink";
- import InfoIcon from "@src/components/ui/InfoIcon";
- import Pagination from "@src/components/ui/Pagination";
- import StatusIcon from "@src/components/ui/StatusComponents/StatusIcon";
- import configLoader from "@src/utils/Config";
- import DateUtils from "@src/utils/DateUtils";
- const Wrapper = styled.div``;
- const Filters = styled.div`
- margin-bottom: 24px;
- display: flex;
- `;
- const FilterDropdownWrapper = styled.div`
- margin-left: 24px;
- `;
- const EventsTable = styled.div`
- background: ${ThemePalette.grayscale[1]};
- border-radius: ${ThemeProps.borderRadius};
- margin-bottom: 16px;
- `;
- const Header = styled.div`
- display: flex;
- border-bottom: 1px solid ${ThemePalette.grayscale[5]};
- padding: 4px 8px;
- `;
- type DataDivProps = {
- width?: string;
- grow?: boolean;
- secondary?: boolean;
- };
- const HeaderData = styled.div<DataDivProps>`
- ${props => (props.width ? ThemeProps.exactWidth(props.width) : "")}
- ${props => (props.grow ? "flex-grow: 1;" : "")}
- font-size: 10px;
- color: ${ThemePalette.grayscale[5]};
- font-weight: ${ThemeProps.fontWeights.medium};
- text-transform: uppercase;
- `;
- const Body = styled.div``;
- const Row = styled.div`
- display: flex;
- padding: 8px;
- border-bottom: 1px solid white;
- `;
- const RowData = styled.div<DataDivProps>`
- ${props => (props.width ? ThemeProps.exactWidth(props.width) : "")}
- ${props => (props.grow ? "flex-grow: 1;" : "")}
- ${props => (props.secondary ? `color: ${ThemePalette.grayscale[4]};` : "")}
- `;
- const Message = styled.pre`
- font-family: inherit;
- white-space: pre-line;
- margin: inherit;
- `;
- const NoData = styled.div`
- text-align: center;
- `;
- type FilterType = "all" | "events" | "progress";
- type EventLevel = "DEBUG" | "INFO" | "ERROR";
- type OrderDir = "asc" | "desc";
- type Props = {
- item?: MinionPoolDetails | null;
- };
- type State = {
- allEvents: MinionPoolEventProgressUpdate[];
- prevLenghts: number[];
- currentPage: number;
- filterBy: FilterType;
- eventLevel: EventLevel;
- orderDir: OrderDir;
- };
- class MinionPoolEvents extends React.Component<Props, State> {
- state = {
- allEvents: [] as MinionPoolEventProgressUpdate[],
- prevLenghts: [0, 0],
- currentPage: 1,
- filterBy: "events" as FilterType,
- eventLevel: "INFO" as EventLevel,
- orderDir: "desc" as OrderDir,
- };
- get filteredEventsWithoutPagination() {
- const shouldFilterByEventType = (event: any): boolean => {
- if (this.state.filterBy === "events") {
- return event.level;
- }
- if (this.state.filterBy === "progress") {
- return event.current_step != null;
- }
- return true;
- };
- const shouldFilterByLevel = (event: any): boolean => {
- if (!event.level) {
- return true;
- }
- if (this.state.eventLevel === "INFO") {
- return event.level === "INFO" || event.level === "ERROR";
- }
- if (this.state.eventLevel === "ERROR") {
- return event.level === "ERROR";
- }
- return true;
- };
- return this.state.allEvents
- .filter(
- (event: any) =>
- shouldFilterByEventType(event) && shouldFilterByLevel(event),
- )
- .sort((a: any, b: any) => {
- if (a.index && b.index && this.state.filterBy !== "all") {
- return this.state.orderDir === "asc"
- ? a.index - b.index
- : b.index - a.index;
- }
- const aTime = new Date(a.created_at).getTime();
- const bTime = new Date(b.created_at).getTime();
- return this.state.orderDir === "asc" ? aTime - bTime : bTime - aTime;
- });
- }
- get filteredEvents() {
- return this.filteredEventsWithoutPagination.filter((_, i) => {
- const minI =
- configLoader.config.maxMinionPoolEventsPerPage *
- (this.state.currentPage - 1);
- const maxI = minI + configLoader.config.maxMinionPoolEventsPerPage;
- return i >= minI && i < maxI;
- });
- }
- static getDerivedStateFromProps(
- props: Props,
- state: State,
- ): Partial<State> | null {
- if (!props.item) {
- return null;
- }
- const events = props.item?.events || [];
- const progressUpdates = props.item?.progress_updates || [];
- if (
- events.length === state.prevLenghts[0] &&
- progressUpdates.length === state.prevLenghts[1]
- ) {
- return null;
- }
- return {
- allEvents: events.concat(progressUpdates as any),
- prevLenghts: [events.length, progressUpdates.length],
- };
- }
- setOrderDir(orderDir: OrderDir) {
- this.setState({ orderDir, currentPage: 1 });
- }
- filterByType(filterBy: FilterType) {
- this.setState({ filterBy, currentPage: 1 });
- }
- filterByLevel(eventLevel: EventLevel) {
- this.setState({ eventLevel, currentPage: 1 });
- }
- handlePreviousPageClick() {
- this.setState(state => ({ currentPage: state.currentPage - 1 }));
- }
- handleNextPageClick() {
- this.setState(state => ({ currentPage: state.currentPage + 1 }));
- }
- renderHeader() {
- return (
- <Header>
- <HeaderData grow>Event / Progress Update Message</HeaderData>
- <HeaderData width="192px">Timestamp</HeaderData>
- </Header>
- );
- }
- renderBody() {
- return (
- <Body>
- {this.filteredEvents.map((event: any) => {
- let status = "INFO";
- status = event.level || status;
- if (event.level === "DEBUG") {
- status = "WARNING";
- }
- const title = event.current_step ? "Progress Update" : "Event";
- return (
- <Row key={event.id}>
- <RowData
- grow
- style={{
- display: "flex",
- alignItems: "center",
- paddingRight: "8px",
- }}
- >
- <StatusIcon
- style={{ marginRight: "8px" }}
- status={status}
- title={title}
- hollow={event.current_step != null}
- />
- <Message>{event.message}</Message>
- </RowData>
- <RowData width="192px" secondary>
- {DateUtils.getLocalDate(event.created_at).toFormat(
- "yyyy-LL-dd HH:mm:ss",
- )}
- </RowData>
- </Row>
- );
- })}
- </Body>
- );
- }
- renderPagination() {
- if (
- this.filteredEventsWithoutPagination.length <=
- configLoader.config.maxMinionPoolEventsPerPage
- ) {
- return null;
- }
- const totalPages = Math.ceil(
- this.filteredEventsWithoutPagination.length /
- configLoader.config.maxMinionPoolEventsPerPage,
- );
- return (
- <Pagination
- previousDisabled={this.state.currentPage === 1}
- nextDisabled={this.state.currentPage === totalPages}
- onPreviousClick={() => {
- this.handlePreviousPageClick();
- }}
- onNextClick={() => {
- this.handleNextPageClick();
- }}
- currentPage={this.state.currentPage}
- totalPages={totalPages}
- />
- );
- }
- renderEventsTable() {
- return (
- <EventsTable>
- {this.renderHeader()}
- {this.renderBody()}
- </EventsTable>
- );
- }
- renderFilters() {
- return (
- <Filters>
- <FilterDropdownWrapper>
- <DropdownLink
- selectedItem={this.state.filterBy}
- items={[
- { label: "Events", value: "events" },
- { label: "Progress Updates", value: "progress" },
- { label: "Events & Progress Updates", value: "all" },
- ]}
- onChange={item => {
- this.filterByType(item.value as FilterType);
- }}
- />
- </FilterDropdownWrapper>
- <FilterDropdownWrapper
- style={{ opacity: this.state.filterBy === "progress" ? 0.5 : 1 }}
- >
- <DropdownLink
- disabled={this.state.filterBy === "progress"}
- selectedItem={this.state.eventLevel}
- items={[
- { label: "DEBUG Event Level", value: "DEBUG" },
- { label: "INFO Event Level", value: "INFO" },
- { label: "ERROR Event Level", value: "ERROR" },
- ]}
- onChange={item => {
- this.filterByLevel(item.value as EventLevel);
- }}
- />
- <InfoIcon text="The log level only applies to the events. The progress updates are not affected." />
- </FilterDropdownWrapper>
- <FilterDropdownWrapper>
- <DropdownLink
- selectedItem={this.state.orderDir}
- items={[
- { label: "Ascending Order", value: "asc" },
- { label: "Descending Order", value: "desc" },
- ]}
- onChange={item => {
- this.setOrderDir(item.value as OrderDir);
- }}
- />
- </FilterDropdownWrapper>
- </Filters>
- );
- }
- renderNoData() {
- return (
- <NoData>
- There are no events or progress updates associated with this minion
- pool.
- </NoData>
- );
- }
- renderNoDataFound() {
- return <NoData>No events found</NoData>;
- }
- render() {
- const isNoData = this.state.allEvents.length === 0;
- const isNoDataFound = this.filteredEvents.length === 0;
- return (
- <Wrapper>
- {!isNoData ? this.renderFilters() : null}
- {!isNoData && !isNoDataFound ? this.renderEventsTable() : null}
- {!isNoData && !isNoDataFound ? this.renderPagination() : null}
- {isNoData ? this.renderNoData() : null}
- {isNoDataFound && !isNoData ? this.renderNoDataFound() : null}
- </Wrapper>
- );
- }
- }
- export default MinionPoolEvents;
|