components/event/EventTable.tsx (320 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import { HStack, Input, Table, Thead, Tbody, Tr, Th, Td, TableContainer, useToast, Box, Button, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, useDisclosure, Select, VStack, Textarea, } from '@chakra-ui/react'; import axios from 'axios'; import { useContext, useEffect, useState } from 'react'; import { CloudEvent } from 'cloudevents'; import { AppContext } from '../../context/context'; interface Topic { name: string, messageCount: number, } interface EventProps { event: CloudEvent<string>, } interface CreateEventRequest { event: CloudEvent<string>, } const CreateEventModal = () => { const { state } = useContext(AppContext); const { isOpen, onOpen, onClose } = useDisclosure(); const [id, setId] = useState(''); const handleIdChange = (event: React.FormEvent<HTMLInputElement>) => { setId(event.currentTarget.value); }; const [source, setSource] = useState(''); const handleSourceChange = (event: React.FormEvent<HTMLInputElement>) => { setSource(event.currentTarget.value); }; const [subject, setSubject] = useState(''); const handleSubjectChange = (event: React.FormEvent<HTMLInputElement>) => { setSubject(event.currentTarget.value); }; const [type, setType] = useState(''); const handleTypeChange = (event: React.FormEvent<HTMLInputElement>) => { setType(event.currentTarget.value); }; const [data, setData] = useState(''); const handleDataChange = (event: React.FormEvent<HTMLInputElement>) => { setData(event.currentTarget.value); }; const toast = useToast(); const [loading, setLoading] = useState(false); const onCreateClick = async () => { try { setLoading(true); await axios.post<CreateEventRequest>(`${state.endpoint}/event`, new CloudEvent({ source, subject, type, data, specversion: '1.0', })); onClose(); } catch (error) { if (axios.isAxiosError(error)) { toast({ title: 'Failed to publish the event', description: error.message, status: 'error', duration: 3000, isClosable: true, }); } } finally { setLoading(false); } }; return ( <> <Button w="25%" colorScheme="blue" onClick={onOpen} > Create Event </Button> <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>Create Event</ModalHeader> <ModalCloseButton /> <ModalBody> <VStack> <Input placeholder="Event ID" value={id} onChange={handleIdChange} /> <Input placeholder="Event Source" value={source} onChange={handleSourceChange} /> <Input placeholder="Event Subject" value={subject} onChange={handleSubjectChange} /> <Input placeholder="Event Type" value={type} onChange={handleTypeChange} /> <Input placeholder="Event Data" value={data} onChange={handleDataChange} /> </VStack> </ModalBody> <ModalFooter> <Button mr={2} onClick={onClose} > Close </Button> <Button colorScheme="blue" onClick={onCreateClick} isLoading={loading} isDisabled={ id.length === 0 || subject.length === 0 || source.length === 0 || type.length === 0 } > Create </Button> </ModalFooter> </ModalContent> </Modal> </> ); }; const EventRow = ({ event, }: EventProps) => { const { isOpen, onOpen, onClose } = useDisclosure(); const eventDataBase64 = event.data_base64 || ''; const eventData = Buffer.from(eventDataBase64, 'base64').toString('utf-8'); return ( <> <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>Event Data</ModalHeader> <ModalCloseButton /> <ModalBody> <Box> <Textarea isDisabled value={eventData} /> </Box> </ModalBody> <ModalFooter> <Button mr={2} onClick={onClose} > Close </Button> </ModalFooter> </ModalContent> </Modal> <Tr> <Td>{event.id}</Td> <Td>{event.subject}</Td> <Td>{new Date(Number(event.reqc2eventmeshtimestamp)).toLocaleString()}</Td> <Td> <HStack> <Button colorScheme="blue" onClick={onOpen} > View Data </Button> </HStack> </Td> </Tr> </> ); }; const EventTable = () => { const { state } = useContext(AppContext); const [searchInput, setSearchInput] = useState<string>(''); const handleSearchInputChange = (event: React.FormEvent<HTMLInputElement>) => { setSearchInput(event.currentTarget.value); }; const [eventList, setEventList] = useState<CloudEvent<string>[]>([]); const [topicList, setTopicList] = useState<Topic[]>([]); const [topic, setTopic] = useState<Topic>({ name: '', messageCount: 0, }); const handleTopicChange = (event: React.FormEvent<HTMLSelectElement>) => { setTopic({ name: event.currentTarget.value, messageCount: 0, }); }; const toast = useToast(); useEffect(() => { const fetch = async () => { try { const { data } = await axios.get<Topic[]>(`${state.endpoint}/topic`); setTopicList(data); if (data.length !== 0) { setTopic(data[0]); } } catch (error) { if (axios.isAxiosError(error)) { toast({ title: 'Failed to fetch the list of events', description: 'unable to connect to the EventMesh daemon', status: 'error', duration: 3000, isClosable: true, }); setEventList([]); } } }; fetch(); }, []); useEffect(() => { const fetch = async () => { try { if (topic.name !== '') { const eventResponse = await axios.get<string[]>(`${state.endpoint}/event`, { params: { topicName: topic.name, offset: 0, length: 15, }, }); setEventList(eventResponse.data.map((rawEvent) => JSON.parse(rawEvent))); } } catch (error) { if (axios.isAxiosError(error)) { toast({ title: 'Failed to fetch the list of events', description: 'Unable to connect to the EventMesh daemon', status: 'error', duration: 3000, isClosable: true, }); setEventList([]); } } }; fetch(); }, [topic]); return ( <Box maxW="full" bg="white" borderWidth="1px" borderRadius="md" overflow="hidden" p="4" > <HStack spacing="2" > <Input w="100%" placeholder="Search" value={searchInput} onChange={handleSearchInputChange} /> <Select w="100%" onChange={handleTopicChange} > {topicList.map(({ name }) => ( <option value={name} key={name} selected={topic.name === name}>{name}</option> ))} </Select> <CreateEventModal /> </HStack> <TableContainer> <Table variant="simple"> <Thead> <Tr> <Th>Event Id</Th> <Th>Event Subject</Th> <Th>Event Time</Th> <Th>Action</Th> </Tr> </Thead> <Tbody> {eventList.filter(() => true).map((event) => ( <EventRow key={event.id} event={event} /> ))} </Tbody> </Table> </TableContainer> </Box> ); }; export default EventTable;