// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Import React, Amplify, and AWS SDK packages import React from 'react'; import { LinkContainer } from 'react-router-bootstrap'; import { API, graphqlOperation, I18n, Storage } from 'aws-amplify'; import { GraphQLResult } from '@aws-amplify/api-graphql'; import { Logger } from '@aws-amplify/core'; import { AmplifyS3Image } from "@aws-amplify/ui-react"; // Import React Bootstrap components import Container from 'react-bootstrap/Container'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import Breadcrumb from 'react-bootstrap/Breadcrumb'; import Button from 'react-bootstrap/Button'; import Form from 'react-bootstrap/Form'; import Card from 'react-bootstrap/Card'; import Jumbotron from 'react-bootstrap/Jumbotron'; import Table from 'react-bootstrap/Table'; import ProgressBar from 'react-bootstrap/ProgressBar'; import Alert from 'react-bootstrap/Alert'; import Modal from 'react-bootstrap/Modal'; // Import graphql import { getProcess } from '../graphql/queries'; // Import custom setting import { LOGGING_LEVEL, sortByName, makeAllVisible, makeVisibleBySearchKeyword } from '../util/CustomUtil'; import GraphQLCommon from '../util/GraphQLCommon'; import { IEvent } from '../components/Interfaces'; import { ModalType, EventPriority, SortBy } from '../components/Enums'; import EmptyRow from '../components/EmptyRow'; /** * Properties Interface * @interface IProps */ interface IProps { history?: any; match?: any; handleNotification: Function; } /** * State Interface * @interface IState */ interface IState { title: string; events: IEvent[]; isLoading: boolean; searchKeyword: string; sort: SortBy; error: string; siteId: string; siteName: string; areaId: string; areaName: string; processId: string; processName: string; eventId: string; eventName: string; eventDescription: string; eventSms: string; eventEmail: string; eventPriority: EventPriority; eventType: string; eventTopicArn: string; modalType: ModalType; modalTitle: string; showModal: boolean; isModalProcessing: boolean; isEventNameValid: boolean; isEventDescriptionValid: boolean; isEventSmsValid: boolean; isEventEmailValid: boolean; isEventTypeValid: boolean; selectAllRootCauses: boolean; eventImgKeys: string[]; eventImgKey: string; eventModalError: string; showEventImageLibrary: boolean; eventAlias: string; isEventAliasValid: boolean; } /** * Types of subscriptions that will be maintained by the main Event class */ export enum EventSubscriptionTypes { CREATE_ROOT_CAUSE, DELETE_ROOT_CAUSE } // Logging const LOGGER = new Logger('Event', LOGGING_LEVEL); /** * The event page * @class Event */ class Event extends React.Component { // GraphQL common class private graphQlCommon: GraphQLCommon; // Create root cause subscription private createRootCauseSubscription: any; // Delete root cause subscription private deleteRootCauseSubscription: any; constructor(props: Readonly) { super(props); this.state = { title: I18n.get('text.events'), events: [], isLoading: false, searchKeyword: '', sort: SortBy.Asc, error: '', siteId: '', siteName: '', areaId: '', areaName: '', processId: '', processName: '', eventId: '', eventName: '', eventDescription: '', eventSms: '', eventEmail: '', eventPriority: EventPriority.Low, eventType: '', eventTopicArn: '', modalType: ModalType.None, modalTitle: '', showModal: false, isModalProcessing: false, isEventNameValid: false, isEventDescriptionValid: false, isEventSmsValid: true, isEventEmailValid: true, isEventTypeValid: true, selectAllRootCauses: false, eventImgKeys: [], eventImgKey: '', eventModalError: '', showEventImageLibrary: false, eventAlias: '', isEventAliasValid: false }; this.graphQlCommon = new GraphQLCommon(); this.deleteEvent = this.deleteEvent.bind(this); this.openModal = this.openModal.bind(this); this.handleSearchKeywordChange = this.handleSearchKeywordChange.bind(this); this.handleSort = this.handleSort.bind(this); this.handleModalClose = this.handleModalClose.bind(this); this.loadEventImages = this.loadEventImages.bind(this); } /** * React componentDidMount function */ async componentDidMount() { // Get process await this.getProcess(); } /** * React componentWillUnmount function */ componentWillUnmount() { if (this.createRootCauseSubscription) this.createRootCauseSubscription.unsubscribe(); if (this.deleteRootCauseSubscription) this.deleteRootCauseSubscription.unsubscribe(); } async loadEventImages() { this.setState({ isModalProcessing: true }); try { const eventImgs = await Storage.list('event-images/', { level: 'public' }); this.setState({ eventImgKeys: eventImgs.map((img: any) => img.key) }); } catch (err) { console.error(err); } this.setState({ isModalProcessing: false }); } /** * Get the process detail. */ async getProcess() { this.setState({ isLoading: true, error: '' }); try { // Graphql operation to get a site const { processId } = this.props.match.params; const response = await API.graphql(graphqlOperation(getProcess, { id: processId })) as GraphQLResult; const data: any = response.data; const resultData = data.getProcess; const siteId = resultData.area.site.id; const siteName = `: ${resultData.area.site.name}`; const areaId = resultData.area.id; const areaName = `: ${resultData.area.name}`; let events: IEvent[] = resultData.event.items; // Make all events visible. makeAllVisible(events); // Sorts initially events.sort((a, b) => a.name.localeCompare(b.name)); this.setState({ siteId, siteName, areaId, areaName, processId, events, title: `${I18n.get('text.events')} (${events.length})` }); } catch (error) { LOGGER.error('Error while getting process', error); this.setState({ error: I18n.get('error.get.process') }); } this.setState({ isLoading: false }); } /** * Delete an event. */ async deleteEvent() { this.setState({ isModalProcessing: true }); try { const { eventId } = this.state; await this.graphQlCommon.deleteEvent(eventId); const updatedEvents = this.state.events.filter(event => event.id !== eventId); this.props.handleNotification(I18n.get('info.delete.event'), 'success', 5); this.setState({ events: updatedEvents, title: `${I18n.get('text.events')} (${updatedEvents.length})`, eventId: '', eventName: '', isModalProcessing: false, showModal: false, modalTitle: '', modalType: ModalType.None }); } catch (error) { let message = I18n.get('error.delete.event'); const castError = error as any; if (castError.errors) { const { errorType } = castError.errors[0]; if (errorType === 'Unauthorized') { message = I18n.get('error.not.authorized'); } } LOGGER.error('Error while delete event', castError); this.props.handleNotification(message, 'error', 5); this.setState({ isModalProcessing: false }); } } /** * Open modal based on type input. * @param {ModalType} modalType- Modal type * @param {IEvent | undefined} event - Event */ async openModal(modalType: ModalType, event?: IEvent) { let modalTitle = ''; if (modalType === ModalType.Delete) { modalTitle = I18n.get('text.delete.event'); } else { this.props.handleNotification(`${I18n.get('error.unsupported.modal.type')}: ${modalType}`, 'warning', 5); return; } let eventId = ''; let eventName = ''; let eventDescription = ''; let eventSms = ''; let eventEmail = ''; let eventTopicArn = ''; let eventPriority = EventPriority.Low; let eventImgKey = ''; if (event) { eventId = event.id ? event.id : ''; eventName = event.name; eventDescription = event.description; eventSms = event.sms ? event.sms : ''; eventEmail = event.email ? event.email : ''; eventImgKey = event.eventImgKey ? event.eventImgKey : ''; for (const priority in EventPriority) { if (priority === event.priority) { eventPriority = EventPriority[priority as keyof typeof EventPriority]; break; } } } await this.loadEventImages(); this.setState({ modalType, modalTitle, eventId, eventName, eventDescription, eventPriority, eventSms, eventEmail, eventTopicArn, eventImgKey, showModal: true, eventModalError: '', showEventImageLibrary: false }); } /** * Get unique root causes from the provided array. * @param {string[]} rootCauses - Array to get unique root causes * @return {string[]} Unique root causes array */ getUniqueRootCauses(rootCauses: string[]): string[] { return Array.from(new Set(rootCauses)); } /** * Handle the search keyword change to filter the events result. * @param {any} event - Event from the search keyword input */ handleSearchKeywordChange(event: any) { const searchKeyword = event.target.value; const { events } = this.state; makeVisibleBySearchKeyword(events, 'name', searchKeyword); this.setState({ events, searchKeyword }); } /** * Handle events sort by site name. * @param {any} event - Event from the sort by select */ handleSort(event: any) { const sort = event.target.value; const events = (sortByName(this.state.events, sort, 'name') as IEvent[]); this.setState({ events, sort }); } /** * Handle modal close. */ handleModalClose() { this.setState({ eventId: '', eventName: '', eventDescription: '', eventEmail: '', eventSms: '', eventPriority: EventPriority.Low, eventType: '', isEventNameValid: false, isEventDescriptionValid: false, isEventSmsValid: true, isEventEmailValid: true, isEventTypeValid: true, selectAllRootCauses: false, showModal: false }); } /** * Render this page. */ render() { return (
{I18n.get('text.sites')} {I18n.get('text.areas')}{this.state.siteName} {I18n.get('info.processes')}{this.state.areaName} {I18n.get('text.events')}{this.state.processName}
{this.state.title}
{I18n.get('text.search.keyword')} {I18n.get('text.sort.by')}
{ this.state.events.length === 0 && !this.state.isLoading &&

{I18n.get('text.no.event')}

} { this.state.events.filter((event: IEvent) => event.visible) .map((event: IEvent) => { let { priority } = event; priority = I18n.get(`text.priority.${priority}`); if (priority.includes('text.priority')) { priority = I18n.get('text.not.found'); } let eventImg; if (event.eventImgKey) { eventImg = (
); } else { eventImg = ''; } return ( {event.name}
{I18n.get('text.description')} {event.description}
{I18n.get('text.sms')} {event.sms}
{I18n.get('text.email')} {event.email}
{I18n.get('text.priority')} {priority}
{I18n.get('text.type')} {event.eventType}
{I18n.get('text.rootcauses')} { event.rootCauses ? `${event.rootCauses.length} ${I18n.get('text.attached.rootcause')}` : '' }
{I18n.get('text.event.id')} {event.id}
{I18n.get('text.event.image')} {eventImg}
{I18n.get('text.event.alias')} {event.alias}
); }) }
{ this.state.isLoading && } { this.state.error && {I18n.get('error')}:
{this.state.error}
}
{this.state.modalTitle} { this.state.modalType === ModalType.Delete &&
{I18n.get('text.confirm.delete.event')}: {this.state.eventName}?
} { this.state.isModalProcessing && }
); } } export default Event;