diff --git a/packages/dashboard/src/components/admin/tests/permissions-card.test.tsx b/packages/dashboard/src/components/admin/tests/permissions-card.test.tsx index dea4fa17d..76b110dab 100644 --- a/packages/dashboard/src/components/admin/tests/permissions-card.test.tsx +++ b/packages/dashboard/src/components/admin/tests/permissions-card.test.tsx @@ -4,10 +4,13 @@ import React from 'react'; import { getActionText, RmfAction } from '../../permissions'; import { PermissionsCard } from '../permissions-card'; +// TODO(AA): To remove after +// https://github.com/testing-library/react-testing-library/issues/1216 +// has been resolved. +// Workaround for "Warning: An update to ComponentName inside a test was not +// wrapped in act(...)." const originalError = console.error; beforeAll(() => { - // this is just a little hack to silence - // Warning: An update to ComponentName inside a test was not wrapped in act(...). console.error = (...args) => { if (/Warning.*not wrapped in act/.test(args[0])) { return; diff --git a/packages/dashboard/src/components/appbar.tsx b/packages/dashboard/src/components/appbar.tsx index 78a39fd29..1aa2c50c0 100644 --- a/packages/dashboard/src/components/appbar.tsx +++ b/packages/dashboard/src/components/appbar.tsx @@ -70,6 +70,7 @@ export type TabValue = 'infrastructure' | 'robots' | 'tasks' | 'custom1' | 'cust const locationToTabValue = (pathname: string): TabValue | undefined => { const routes: { prefix: string; tabValue: TabValue }[] = [ + { prefix: DashboardRoute, tabValue: 'infrastructure' }, { prefix: RobotsRoute, tabValue: 'robots' }, { prefix: TasksRoute, tabValue: 'tasks' }, { prefix: CustomRoute1, tabValue: 'custom1' }, @@ -79,7 +80,7 @@ const locationToTabValue = (pathname: string): TabValue | undefined => { // `DashboardRoute` being the root, it is a prefix to all routes, so we need to check exactly. const matchingRoute = routes.find((route) => pathname.startsWith(route.prefix)); - return pathname === DashboardRoute ? 'infrastructure' : matchingRoute?.tabValue; + return matchingRoute?.tabValue; }; function AppSettings() { diff --git a/packages/react-components/lib/commands/command-forms.stories.tsx b/packages/react-components/lib/commands/command-forms.stories.tsx deleted file mode 100644 index 710e9551d..000000000 --- a/packages/react-components/lib/commands/command-forms.stories.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { DeliveryRequestForm } from './delivery-request-form'; -import { LoopRequestForm } from './loop-request-form'; -import { availableDispensers, availablePlaces, fleets } from './test-data.spec'; - -export default { - title: 'Command Forms', - argTypes: { - doLoopRequest: { action: 'loop request' }, - doDeliveryRequest: { action: 'delivery request' }, - }, -} as Meta; - -export const DeliveryRequest: Story = (args) => ( - -); - -export const LoopRequest: Story = (args) => ( - -); diff --git a/packages/react-components/lib/commands/delivery-request-form.spec.tsx b/packages/react-components/lib/commands/delivery-request-form.spec.tsx deleted file mode 100644 index 892c75239..000000000 --- a/packages/react-components/lib/commands/delivery-request-form.spec.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { render, screen, within, fireEvent } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { DeliveryRequestForm } from './delivery-request-form'; -import { availableDispensers, availablePlaces, fleets } from './test-data.spec'; - -describe('Form validation', () => { - let fakeDoDeliveryRequest: ReturnType; - - function renderForm() { - fakeDoDeliveryRequest = jasmine.createSpy(); - } - - beforeEach(() => { - renderForm(); - }); - - it('Successful Request', () => { - // TODO [CR]: FIX ERROR - // Error: Expected spy unknown to have been called. - // Comment test for now Jul 12th - // render( - // , - // ); - // userEvent.type(screen.getByPlaceholderText('Pickup Dispenser'), 'dispenserA'); - // fireEvent.click(screen.getByText('Request')); - // expect(fakeDoDeliveryRequest).toHaveBeenCalled(); - }); - - it('Pickup dispenser cannot be empty', () => { - const { container } = render( - , - ); - userEvent.type(screen.getByPlaceholderText('Pickup Dispenser'), '{selectall}{backspace}'); - - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoDeliveryRequest).not.toHaveBeenCalled(); - }); - - it('Dropoff dispenser cannot be empty', () => { - const { container } = render( - , - ); - userEvent.type( - screen.getByPlaceholderText('Pick Drop Off Dispenser'), - '{selectall}{backspace}', - ); - - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoDeliveryRequest).not.toHaveBeenCalled(); - }); - - it('Pickup place cannot be empty', () => { - const { container } = render( - , - ); - userEvent.type(screen.getByPlaceholderText('Pick Start Location'), '{selectall}{backspace}'); - - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoDeliveryRequest).not.toHaveBeenCalled(); - }); - - it('Dropoff place cannot be empty', () => { - render( - , - ); - userEvent.type(screen.getByPlaceholderText('Pick Drop Off Location'), '{selectall}{backspace}'); - fireEvent.click(screen.getByText('Request')); - expect(fakeDoDeliveryRequest).not.toHaveBeenCalled(); - }); - - it('shows error when a place with no dispenser is picked', async () => { - const { container } = render( - , - ); - const autocompleteFleet = screen.getByTestId('autocomplete Target Fleet'); - const inputFleet = within(autocompleteFleet).getByPlaceholderText( - 'Choose Target Fleet', - ) as HTMLInputElement; - autocompleteFleet.focus(); - fireEvent.change(inputFleet, { target: { value: 'fleetB' } }); - fireEvent.keyDown(autocompleteFleet, { key: 'Enter' }); - - const autocompleteStartLocation = screen.getByTestId('autocomplete Start Location'); - const inputStartLocation = within(autocompleteStartLocation).getByPlaceholderText( - 'Pick Start Location', - ) as HTMLInputElement; - autocompleteStartLocation.focus(); - fireEvent.change(inputStartLocation, { target: { value: 'placeB' } }); - fireEvent.keyDown(autocompleteStartLocation, { key: 'Enter' }); - - const autocompleteDispenser = screen.getByTestId('autocomplete pickup dispenser'); - const inputPickupDispenser = within(autocompleteDispenser).getByPlaceholderText( - 'Pickup Dispenser', - ) as HTMLInputElement; - autocompleteDispenser.focus(); - fireEvent.change(inputPickupDispenser, { target: { value: 'dispenserB' } }); - fireEvent.keyDown(autocompleteDispenser, { key: 'Enter' }); - - const autocompleteDropOffLocation = screen.getByTestId('autocomplete-dispenser'); - const inputDropOffLocation = within(autocompleteDropOffLocation).getByPlaceholderText( - 'Pick Drop Off Location', - ) as HTMLInputElement; - autocompleteDropOffLocation.focus(); - fireEvent.change(inputDropOffLocation, { target: { value: 'placeC' } }); - fireEvent.keyDown(autocompleteDropOffLocation, { key: 'Enter' }); - - expect(!container.querySelector('.MuiFormHelperText-root.Mui-error')).toBe(true); - }); - - it('Pickup dispenser cannot be equal to dropoff dispenser', () => { - const { container } = render( - , - ); - const autocomplete = screen.getByTestId('autocomplete-dispenser'); - const input = within(autocomplete).getByPlaceholderText( - 'Pick Drop Off Location', - ) as HTMLInputElement; - autocomplete.focus(); - fireEvent.change(input, { target: { value: 'placeA' } }); - fireEvent.keyDown(autocomplete, { key: 'Enter' }); - fireEvent.click(screen.getByText('Request')); - - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoDeliveryRequest).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/commands/delivery-request-form.tsx b/packages/react-components/lib/commands/delivery-request-form.tsx deleted file mode 100644 index 2963c5efe..000000000 --- a/packages/react-components/lib/commands/delivery-request-form.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { Button, TextField, Autocomplete } from '@mui/material'; -import React, { ChangeEvent } from 'react'; -import { StyledForm, commandFormsClasses } from './form-styles'; - -export type DoDeliveryRequest = ( - pickupPlaceName: string, - pickupDispenser: string, - dropOffPlaceName: string, - dropOffDispenser: string, -) => void; - -export interface DeliveryRequestFormProps { - fleetNames: string[]; - availablePlaces(fleet: string): string[]; - availableDispensers(fleet: string, place: string): string[]; - doDeliveryRequest?: DoDeliveryRequest; -} - -export const DeliveryRequestForm = React.forwardRef( - (props: DeliveryRequestFormProps, ref: React.Ref): JSX.Element => { - const { fleetNames, availableDispensers, availablePlaces, doDeliveryRequest } = props; - - const [targetFleetName, setTargetFleetName] = React.useState( - fleetNames.length >= 1 ? fleetNames[0] : '', - ); - const [listOfPlaces, setListOfPlaces] = React.useState( - targetFleetName ? availablePlaces(targetFleetName) : [], - ); - - // Places - const [pickupPlaceName, setPickupPlaceName] = React.useState( - !!listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[0] : '', - ); - const [dropOffPlaceName, setDropOffPlaceName] = React.useState( - !!listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[1] : '', - ); - - // Dispensers - const [pickupDispenser, setPickupDispenser] = React.useState(''); - const [dropOffDispenser, setDropOffDispenser] = React.useState(''); - - // Error states - const [targetFleetNameError, setTargetFleetNameError] = React.useState(''); - const [pickupPlaceNameError, setPickupPlaceNameError] = React.useState(''); - const [pickupDispenserError, setPickupDispenserError] = React.useState(''); - const [dropOffPlaceNameError, setDropOffPlaceNameError] = React.useState(''); - const [dropOffDispenserError, setDropOffDispenserError] = React.useState(''); - - const cleanUpForm = (): void => { - setTargetFleetName(fleetNames.length >= 1 ? fleetNames[0] : ''); - setPickupPlaceName(listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[0] : ''); - setDropOffPlaceName(listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[1] : ''); - setPickupDispenser(''); - setDropOffDispenser(''); - cleanUpError(); - }; - - const cleanUpError = (): void => { - setTargetFleetNameError(''); - setPickupPlaceNameError(''); - setDropOffPlaceNameError(''); - setPickupDispenserError(''); - setDropOffDispenserError(''); - }; - - const handleSubmit = (ev: React.FormEvent): void => { - ev.preventDefault(); - if (isFormValid()) { - doDeliveryRequest && - doDeliveryRequest(pickupPlaceName, pickupDispenser, dropOffPlaceName, dropOffDispenser); - cleanUpForm(); - } - }; - - const dispensersFromPickUpPlace = React.useMemo(() => { - const dispenser = pickupPlaceName - ? availableDispensers(targetFleetName, pickupPlaceName) - : []; - return dispenser ? dispenser : []; - }, [pickupPlaceName, targetFleetName, availableDispensers]); - - const dispensersFromDropOffPlace = React.useMemo(() => { - const dispenser = dropOffPlaceName - ? availableDispensers(targetFleetName, dropOffPlaceName) - : []; - return dispenser ? dispenser : []; - }, [dropOffPlaceName, targetFleetName, availableDispensers]); - - React.useEffect(() => { - setPickupDispenserError(''); - !!dispensersFromPickUpPlace && - dispensersFromPickUpPlace.length === 0 && - setPickupDispenserError('There is no dispensers on this place. Pick another place'); - }, [dispensersFromPickUpPlace]); - - React.useEffect(() => { - setDropOffDispenserError(''); - !!dispensersFromDropOffPlace && - dispensersFromDropOffPlace.length === 0 && - setDropOffDispenserError('There is no dispensers on this place. Pick another place'); - }, [dispensersFromDropOffPlace]); - - const isFormValid = (): boolean => { - let isValid = true; - cleanUpError(); - - if (targetFleetName === '') { - setTargetFleetNameError('Fleet name cannot be empty'); - isValid = false; - } - - if (pickupPlaceName === dropOffPlaceName) { - setPickupPlaceNameError('Start Location cannot be equal to finish Location'); - setDropOffPlaceNameError('Start Location cannot be equal to finish Location'); - isValid = false; - } - - if (pickupPlaceName === dropOffPlaceName) { - setPickupDispenserError('Pickup dispenser cannot be equal to Drop off dispenser'); - setDropOffDispenserError('Drop off dispenser cannot be equal to Pickup dispenser'); - isValid = false; - } - - const setEmpty = (fieldSetter: React.Dispatch>): void => { - fieldSetter('Cannot be empty'); - isValid = false; - }; - - !pickupPlaceName && setEmpty(setPickupPlaceNameError); - !dropOffPlaceName && setEmpty(setDropOffPlaceNameError); - !pickupDispenser && setEmpty(setPickupDispenserError); - !dropOffDispenser && setEmpty(setDropOffDispenserError); - - return isValid; - }; - - const handleTargetFleetNameChange = (_: ChangeEvent, value: string | null) => { - const newFleetName = value || fleetNames[0]; - const newPlaces = availablePlaces(newFleetName); - setPickupPlaceName((cur) => { - if (newPlaces.includes(cur)) { - return cur; - } - return newPlaces.length >= 2 ? newPlaces[0] : ''; - }); - setPickupDispenser(''); - setDropOffPlaceName((cur) => { - if (newPlaces.includes(cur)) { - return cur; - } - return newPlaces.length >= 2 ? newPlaces[1] : ''; - }); - setDropOffDispenser(''); - setListOfPlaces(availablePlaces(newFleetName)); - setTargetFleetName(newFleetName); - }; - - const handlePickupPlaceNameChange = (_: ChangeEvent, value: string | null) => { - setPickupPlaceName(value || ''); - setPickupDispenser(''); - }; - - const handleDropOoffPlaceNameChange = (_: ChangeEvent, value: string | null) => { - setDropOffPlaceName(value || ''); - setDropOffDispenser(''); - }; - - return ( - -
- option} - onChange={handleTargetFleetNameChange} - options={fleetNames} - renderInput={(params) => ( - - )} - value={targetFleetName ? targetFleetName : null} - /> -
- -
- option} - onChange={handlePickupPlaceNameChange} - options={listOfPlaces ? listOfPlaces : []} - renderInput={(params) => ( - - )} - value={pickupPlaceName ? pickupPlaceName : null} - /> -
- -
- option} - onChange={(_, value) => setPickupDispenser(value || '')} - options={dispensersFromPickUpPlace} - renderInput={(params) => ( - - )} - value={pickupDispenser ? pickupDispenser : null} - /> -
- -
- option} - onChange={handleDropOoffPlaceNameChange} - options={listOfPlaces ? listOfPlaces : []} - renderInput={(params) => ( - - )} - value={dropOffPlaceName ? dropOffPlaceName : null} - /> -
- -
- option} - onChange={(_, value) => setDropOffDispenser(value || '')} - options={dispensersFromDropOffPlace} - renderInput={(params) => ( - - )} - value={dropOffDispenser ? dropOffDispenser : null} - /> -
- -
- -
-
- ); - }, -); - -export default DeliveryRequestForm; diff --git a/packages/react-components/lib/commands/form-styles.ts b/packages/react-components/lib/commands/form-styles.ts deleted file mode 100644 index d985daaf4..000000000 --- a/packages/react-components/lib/commands/form-styles.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { styled } from '@mui/material'; - -// TODO - refactor classes base on the function of the class -// instead of component name, for eg, command-form-input would -// be full-width as it specify width of component to be 100% -export const commandFormsClasses = { - form: 'command-form-root', - divForm: 'command-form-div-wrapper', - input: 'command-form-input', - button: 'command-form-button', - buttonContainer: 'command-form-button-container', -}; -export const StyledForm = styled('form')(({ theme }) => ({ - [`&.${commandFormsClasses.form}`]: { - display: 'flex', - flexDirection: 'column', - width: '100%', - backgroundColor: theme.palette.background.paper, - }, - [`& .${commandFormsClasses.divForm}`]: { - padding: '0.46rem', - paddingRight: '0.5rem', - width: '100%', - }, - [`& .${commandFormsClasses.input}`]: { - width: '100%', - }, - [`& .${commandFormsClasses.button}`]: { - width: '100%', - }, - [`& .${commandFormsClasses.buttonContainer}`]: { - paddingTop: '0.5rem', - paddingLeft: '0.5rem', - width: '100%', - }, -})); diff --git a/packages/react-components/lib/commands/index.ts b/packages/react-components/lib/commands/index.ts deleted file mode 100644 index eeced9528..000000000 --- a/packages/react-components/lib/commands/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './delivery-request-form'; -export * from './loop-request-form'; diff --git a/packages/react-components/lib/commands/loop-request-form.spec.tsx b/packages/react-components/lib/commands/loop-request-form.spec.tsx deleted file mode 100644 index ace3e751a..000000000 --- a/packages/react-components/lib/commands/loop-request-form.spec.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { render, screen, within, fireEvent } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { LoopRequestForm } from './loop-request-form'; -import { availablePlaces, fleets } from './test-data.spec'; - -describe('Form validation', () => { - let fakeDoLoopRequest: ReturnType; - - function renderForm() { - fakeDoLoopRequest = jasmine.createSpy(); - } - - beforeEach(() => { - renderForm(); - }); - - it('Successful Request', () => { - render( - , - ); - userEvent.type(screen.getByPlaceholderText('Number of loops'), '1'); - fireEvent.click(screen.getByText('Request')); - expect(fakeDoLoopRequest).toHaveBeenCalled(); - }); - - it('Number of loops cannot be empty', () => { - const { container } = render( - , - ); - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoLoopRequest).not.toHaveBeenCalled(); - }); - - it('Start Location cannot be empty', () => { - const { container } = render( - , - ); - userEvent.type(screen.getByPlaceholderText('Number of loops'), '1'); - userEvent.type(screen.getByPlaceholderText('Pick Start Location'), '{selectall}{backspace}'); - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoLoopRequest).not.toHaveBeenCalled(); - }); - - it('Finish Location cannot be empty', () => { - const { container } = render( - , - ); - userEvent.type(screen.getByPlaceholderText('Number of loops'), '1'); - userEvent.type(screen.getByPlaceholderText('Pick Finish Location'), '{selectall}{backspace}'); - fireEvent.click(screen.getByText('Request')); - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoLoopRequest).not.toHaveBeenCalled(); - }); - - it('Start Location cannot be equal to Finish Location', () => { - const { container } = render( - , - ); - const autocomplete = screen.getByTestId('autocomplete-location'); - const input = within(autocomplete).getByPlaceholderText( - 'Pick Start Location', - ) as HTMLInputElement; - autocomplete.focus(); - fireEvent.change(input, { target: { value: 'placeA' } }); - fireEvent.keyDown(autocomplete, { key: 'Enter' }); - fireEvent.click(screen.getByText('Request')); - - expect(container.querySelector('.MuiFormHelperText-root.Mui-error')).toBeTruthy(); - expect(fakeDoLoopRequest).not.toHaveBeenCalled(); - }); - - it('Changing target fleet updates available places', async () => { - render( - , - ); - const autocomplete = screen.getByTestId('autocomplete-fleet'); - const input = within(autocomplete).getByPlaceholderText( - 'Choose Target Fleet', - ) as HTMLInputElement; - autocomplete.focus(); - fireEvent.change(input, { target: { value: 'fleetB' } }); - fireEvent.keyDown(autocomplete, { key: 'ArrowDown' }); - fireEvent.keyDown(autocomplete, { key: 'Enter' }); - expect(input.value).toEqual('fleetB'); - }); -}); diff --git a/packages/react-components/lib/commands/loop-request-form.tsx b/packages/react-components/lib/commands/loop-request-form.tsx deleted file mode 100644 index 30fe5b70f..000000000 --- a/packages/react-components/lib/commands/loop-request-form.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { Button, TextField, Autocomplete } from '@mui/material'; -import React, { ChangeEvent } from 'react'; -import { StyledForm, commandFormsClasses } from './form-styles'; - -export type DoLoopRequest = ( - fleetName: string, - numLoops: number, - startLocationPoint: string, - endLocationPoint: string, -) => void; - -export interface LoopRequestFormProps { - fleetNames: string[]; - availablePlaces(fleet: string): string[]; - doLoopRequest?: DoLoopRequest; -} - -export const LoopRequestForm = React.forwardRef( - (props: LoopRequestFormProps, ref: React.Ref): JSX.Element => { - const { fleetNames, availablePlaces, doLoopRequest } = props; - - const [targetFleetName, setTargetFleetName] = React.useState( - fleetNames.length >= 1 ? fleetNames[0] : '', - ); - - const [numLoops, setNumLoops] = React.useState(0); - const [listOfPlaces, setListOfPlaces] = React.useState( - targetFleetName ? availablePlaces(targetFleetName) : [], - ); - const [startLocation, setStartLocation] = React.useState( - listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[0] : '', - ); - const [finishLocation, setFinishLocation] = React.useState( - listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[1] : '', - ); - - React.useLayoutEffect(() => { - if (listOfPlaces) { - setStartLocation(listOfPlaces.length >= 2 ? listOfPlaces[0] : ''); - setFinishLocation(listOfPlaces.length >= 2 ? listOfPlaces[1] : ''); - } - }, [listOfPlaces]); - - // Error states - const [targetFleetNameError, setTargetFleetNameError] = React.useState(''); - const [numLoopsError, setNumLoopsError] = React.useState(''); - const [startLocationError, setStartLocationError] = React.useState(''); - const [finishLocationError, setFinishLocationError] = React.useState(''); - - const cleanUpForm = () => { - setTargetFleetName(targetFleetName); - setNumLoops(0); - setListOfPlaces(targetFleetName ? availablePlaces(targetFleetName) : []); - setStartLocation(listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[0] : ''); - setFinishLocation(listOfPlaces && listOfPlaces.length >= 2 ? listOfPlaces[1] : ''); - cleanUpError(); - }; - - const cleanUpError = () => { - setTargetFleetNameError(''); - setNumLoopsError(''); - setStartLocationError(''); - setFinishLocationError(''); - }; - - const isFormValid = () => { - let isValid = true; - cleanUpError(); - if (targetFleetName === '') { - setTargetFleetNameError('Fleet name cannot be empty'); - isValid = false; - } - if (numLoops === 0 || numLoops < 0) { - setNumLoopsError('Loops can only be > 0'); - isValid = false; - } - if (startLocation === finishLocation) { - setStartLocationError('Start Location cannot be equal to Finish Location'); - setFinishLocationError('Start Location cannot be equal to Finish Location'); - isValid = false; - } - - if (!startLocation) { - setStartLocationError('Location cannot be empty'); - isValid = false; - } - if (!finishLocation) { - setFinishLocationError('Location cannot be empty'); - isValid = false; - } - - return isValid; - }; - - const handleTargetFleetNameChange = (_: ChangeEvent, value: string | null) => { - const newFleetName = value || fleetNames[0]; - const newPlaces = availablePlaces(newFleetName) || []; - setListOfPlaces(newPlaces); - setStartLocation((cur) => { - if (newPlaces.includes(cur)) { - return cur; - } - return newPlaces.length >= 2 ? newPlaces[0] : ''; - }); - setFinishLocation((cur) => { - if (newPlaces.includes(cur)) { - return cur; - } - return newPlaces.length >= 2 ? newPlaces[1] : ''; - }); - setTargetFleetName(newFleetName); - }; - - const handleSubmit = (ev: React.FormEvent) => { - ev.preventDefault(); - if (isFormValid()) { - doLoopRequest && doLoopRequest(targetFleetName, numLoops, startLocation, finishLocation); - cleanUpForm(); - } - }; - - return ( - -
- option} - onChange={handleTargetFleetNameChange} - options={fleetNames} - data-testid="autocomplete-fleet" - renderInput={(params) => ( - - )} - value={targetFleetName ? targetFleetName : null} - /> -
-
- { - setNumLoops(e.target.value ? parseInt(e.target.value) : 0); - }} - placeholder="Number of loops" - type="number" - value={numLoops || ''} - className={commandFormsClasses.input} - label="Number of loops" - variant="outlined" - error={!!numLoopsError} - helperText={numLoopsError} - /> -
- -
- option} - onChange={(_, value) => setStartLocation(value || '')} - options={listOfPlaces ? listOfPlaces : []} - renderInput={(params) => ( - - )} - value={startLocation ? startLocation : null} - /> -
- -
- option} - onChange={(_, value) => setFinishLocation(value || '')} - options={listOfPlaces ? listOfPlaces : []} - renderInput={(params) => ( - - )} - value={finishLocation ? finishLocation : null} - /> -
- -
- -
-
- ); - }, -); - -export default LoopRequestForm; diff --git a/packages/react-components/lib/commands/test-data.spec.ts b/packages/react-components/lib/commands/test-data.spec.ts deleted file mode 100644 index 90e686f61..000000000 --- a/packages/react-components/lib/commands/test-data.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const fleets = ['fleetA', 'fleetB']; - -const availablePlacesData: Record = { - fleetA: ['placeA', 'placeB'], - fleetB: ['placeB', 'placeC'], -}; - -export function availablePlaces(fleet: string): string[] { - return availablePlacesData[fleet] || []; -} - -const availableDispensersData: Record> = { - fleetA: { - placeA: ['dispenserA'], - placeB: ['dispenserB', 'dispenserBB'], - }, - fleetB: { - placeB: ['dispenserB'], - placeC: [], - }, -}; - -export function availableDispensers(fleet: string, place: string): string[] { - const placeMap = availableDispensersData[fleet] || {}; - if (!Object.keys(placeMap).length) { - return []; - } - return placeMap[place] || []; -} diff --git a/packages/react-components/lib/index.ts b/packages/react-components/lib/index.ts index 73cb65881..f0cb40b9b 100644 --- a/packages/react-components/lib/index.ts +++ b/packages/react-components/lib/index.ts @@ -8,7 +8,6 @@ import '@mui/system'; export * from './appbar-tab'; export * from './alert-dialog'; export * from './color-manager'; -export * from './commands'; export * from './confirmation-dialog'; export * from './doors'; export * from './error-overlay'; @@ -21,7 +20,6 @@ export * from './logo-button'; export * from './map'; export * from './navigation-bar'; export * from './place'; -export * from './reports'; export * from './robots'; export * from './simple-filter'; export * from './simple-info'; diff --git a/packages/react-components/lib/reports/default-date-form.spec.tsx b/packages/react-components/lib/reports/default-date-form.spec.tsx deleted file mode 100644 index f30a4d8e6..000000000 --- a/packages/react-components/lib/reports/default-date-form.spec.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../test/locale'; -import { DefaultDatesForm } from './default-dates-form'; -import { reportConfigProps } from './utils.spec'; - -describe('Default date form', () => { - it('places correctly initial values', () => { - const date = new Date(); - const newConfig = { ...reportConfigProps, fromLogDate: date, toLogDate: date }; - const root = render(, { wrapper: TestLocalizationProvider }); - const fromInput = root.getByLabelText('From'); - expect(fromInput.getAttribute('data-unix')).toBe(date.valueOf().toString()); - const toInput = root.getByLabelText('To'); - expect(toInput.getAttribute('data-unix')).toBe(date.valueOf().toString()); - }); -}); diff --git a/packages/react-components/lib/reports/default-dates-form.tsx b/packages/react-components/lib/reports/default-dates-form.tsx deleted file mode 100644 index 300f54b8a..000000000 --- a/packages/react-components/lib/reports/default-dates-form.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { styled, TextField } from '@mui/material'; -import Button from '@mui/material/Button'; -import React from 'react'; -import { LogQueryPayload } from '.'; - -interface DefaultDatesFormProps { - search?: (payload: LogQueryPayload) => void; - fromLogDate?: Date; - toLogDate?: Date; - onSelectFromDate?: (date: unknown) => void; - onSelectToDate?: (date: unknown) => void; -} - -const classes = { - searchForm: 'traj-time-control-root', - searchButton: 'traj-time-control-container', -}; -const StyledDiv = styled('div')(() => ({ - [`& .${classes.searchForm}`]: { - display: 'flex', - justifyContent: 'space-evenly', - }, - [`& .${classes.searchButton}`]: { - width: '100%', - }, -})); - -export const DefaultDatesForm = (props: DefaultDatesFormProps): JSX.Element | null => { - const { search, fromLogDate, toLogDate, onSelectToDate, onSelectFromDate } = props; - const searchQuery = () => { - search && search({ toLogDate, fromLogDate }); - }; - - return onSelectFromDate && onSelectToDate ? ( - -
- ( - - )} - /> - ( - - )} - /> -
- -

- -
- ) : null; -}; diff --git a/packages/react-components/lib/reports/default-report-interface.tsx b/packages/react-components/lib/reports/default-report-interface.tsx deleted file mode 100644 index 1f0395004..000000000 --- a/packages/react-components/lib/reports/default-report-interface.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { styled } from '@mui/material'; - -export const defaultReportClasses = { - table: 'default-report-table', -}; -export const DefaultReportContainer = styled('div')(() => ({ - [`& .${defaultReportClasses.table}`]: { - overflowY: 'scroll', - paddingTop: '20px', - }, -})); - -export interface DefaultReportQueryPayload { - toLogDate?: unknown | null; - fromLogDate?: unknown | null; - offset?: number | null; - limit?: number | null; -} - -export interface DefaultLogTableProps { - tableSize?: number; - addMoreRows?(): void; -} diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx deleted file mode 100644 index 195ee4ebe..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getDispenserLogs } from '../utils.spec'; -import { DispenserStateReportTable } from './dispenser-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Dispenser table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('dispenser-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Guid')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx deleted file mode 100644 index b81d59926..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type DispenserStateRowsType = { - created: string; //date - guid: string; - state: string; -}[]; - -export interface DispenserStateReportTable extends DefaultLogTableProps { - rows: DispenserStateRowsType | []; -} - -export const DispenserStateReportTable = (props: DispenserStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.guid} - columns={[ - { - headerName: 'Guid', - field: 'guid', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.guid}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx deleted file mode 100644 index a39448bf0..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { getDispenserLogs, reportConfigProps } from '../utils.spec'; -import { DispenserStateReport } from './dispenser-state-report'; -import { TestLocalizationProvider } from '../../test/locale'; - -const getLogsPromise = async () => getDispenserLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Dispenser State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getDispenserLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx deleted file mode 100644 index 28af636c3..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { DispenserStateReportTable, DispenserStateRowsType } from './dispenser-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface DispenserStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const DispenserStateReport = (props: DispenserStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default DispenserStateReport; diff --git a/packages/react-components/lib/reports/dispenser-state/index.ts b/packages/react-components/lib/reports/dispenser-state/index.ts deleted file mode 100644 index f3522dc5f..000000000 --- a/packages/react-components/lib/reports/dispenser-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './dispenser-state-report'; -export * from './dispenser-state-report-table'; diff --git a/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx b/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx deleted file mode 100644 index aeeb7360d..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getDoorLogs } from '../utils.spec'; -import { DoorStateReportTable } from './door-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Door table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('door-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Name')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/door-state/door-state-report-table.tsx b/packages/react-components/lib/reports/door-state/door-state-report-table.tsx deleted file mode 100644 index efc677813..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report-table.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type DoorStateRowsType = { - created: string; //date - door: { id: number; name: string }; - state: string; -}[]; - -export interface DoorStateReportTable extends DefaultLogTableProps { - rows: DoorStateRowsType | []; -} - -export const DoorStateReportTable = (props: DoorStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - return ( -
- r.door.id} - columns={[ - { - headerName: 'Name', - field: 'name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.door.name}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx b/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx deleted file mode 100644 index 3210ea71f..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getDoorLogs, reportConfigProps } from '../utils.spec'; -import { DoorStateReport } from './door-state-report'; - -const getLogsPromise = async () => getDoorLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Door State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getDoorLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/door-state/door-state-report.tsx b/packages/react-components/lib/reports/door-state/door-state-report.tsx deleted file mode 100644 index 7c7d837e0..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { DoorStateReportTable, DoorStateRowsType } from './door-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface DoorStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const DoorStateReport = (props: DoorStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default DoorStateReport; diff --git a/packages/react-components/lib/reports/door-state/index.ts b/packages/react-components/lib/reports/door-state/index.ts deleted file mode 100644 index 7b4b3a528..000000000 --- a/packages/react-components/lib/reports/door-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './door-state-report'; -export * from './door-state-report-table'; diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx deleted file mode 100644 index f97476b45..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getFleetLogs } from '../utils.spec'; -import { FleetStateReportTable } from './fleet-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Fleet table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('fleet-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Fleet')).toBeTruthy(); - expect(screen.queryByText('Robot')).toBeTruthy(); - expect(screen.queryByText('Battery')).toBeTruthy(); - expect(screen.queryByText('Mode')).toBeTruthy(); - expect(screen.queryByText('Model')).toBeTruthy(); - expect(screen.queryByText('TaskID')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx deleted file mode 100644 index c19a332b2..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type FleetStateRowsType = { - created: string; //date - fleet: { id: number; name: string }; - robot: { id: number; name: string; model?: string }; - robot_battery_percent: string; - robot_location: string; - robot_mode: string; - robot_seq: number; - robot_task_id: string; -}[]; - -export interface FleetStateReportTable extends DefaultLogTableProps { - rows: FleetStateRowsType | []; -} - -export const FleetStateReportTable = (props: FleetStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.robot.id} - columns={[ - { - headerName: 'Fleet', - field: 'fleet_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.fleet.name}; - }, - }, - { - headerName: 'Robot', - field: 'robot_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.name}; - }, - }, - { - headerName: 'Battery', - field: 'robot_battery_percent', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_battery_percent}; - }, - }, - { - headerName: 'Mode', - field: 'robot_mode', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_mode}; - }, - }, - { - headerName: 'Model', - field: 'robot_model', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.model}; - }, - }, - { - headerName: 'TaskID', - field: 'robot_task_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_task_id}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx deleted file mode 100644 index 5379f0820..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getFleetLogs, reportConfigProps } from '../utils.spec'; -import { FleetStateReport } from './fleet-state-report'; - -describe('Fleet State report', () => { - const getLogsPromise = async () => getFleetLogs(); - - it('smoke test', async () => { - await waitFor(() => { - render(); - }); - }); - - it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Fleet State')).toBeFalsy(); - }); - - it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getFleetLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx deleted file mode 100644 index ebdbbc503..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - DefaultReportContainer, - defaultReportClasses, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { FleetStateReportTable, FleetStateRowsType } from './fleet-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface FleetStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const FleetStateReport = (props: FleetStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default FleetStateReport; diff --git a/packages/react-components/lib/reports/fleet-state/index.ts b/packages/react-components/lib/reports/fleet-state/index.ts deleted file mode 100644 index 02939fe93..000000000 --- a/packages/react-components/lib/reports/fleet-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './fleet-state-report'; -export * from './fleet-state-report-table'; diff --git a/packages/react-components/lib/reports/health/health-report-table.spec.tsx b/packages/react-components/lib/reports/health/health-report-table.spec.tsx deleted file mode 100644 index aab2b3749..000000000 --- a/packages/react-components/lib/reports/health/health-report-table.spec.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getHealthLogs } from '../utils.spec'; -import { HealthReportTable } from './health-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Health table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('health-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Device')).toBeTruthy(); - expect(screen.queryByText('Actor')).toBeTruthy(); - expect(screen.queryByText('Health Status')).toBeTruthy(); - expect(screen.queryByText('Health Message')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/health/health-report-table.tsx b/packages/react-components/lib/reports/health/health-report-table.tsx deleted file mode 100644 index 75c7363c4..000000000 --- a/packages/react-components/lib/reports/health/health-report-table.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type HealthRowsType = { - created: string; //date - device: { id: number; type: string; actor: string }; - health_status: string; - health_message: string; -}[]; - -export interface HealthReportTable extends DefaultLogTableProps { - rows: HealthRowsType | []; -} - -export const HealthReportTable = (props: HealthReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.device.id} - columns={[ - { - headerName: 'Device', - field: 'device', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.device.type}; - }, - }, - { - headerName: 'Actor', - field: 'actor_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.device.actor}; - }, - }, - { - headerName: 'Health Status', - field: 'health_status', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.health_status}; - }, - }, - { - headerName: 'Health Message', - field: 'health_message', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.health_message}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/health/health-report.spec.tsx b/packages/react-components/lib/reports/health/health-report.spec.tsx deleted file mode 100644 index 5c4685add..000000000 --- a/packages/react-components/lib/reports/health/health-report.spec.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getHealthLogs, reportConfigProps } from '../utils.spec'; -import { HealthReport } from './health-report'; - -describe('Health State report', () => { - const getLogsPromise = async () => getHealthLogs(); - - it('smoke test', async () => { - await waitFor(() => { - render(); - }); - }); - - it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( await []} />); - }); - - expect(screen.queryByText('Health')).toBeFalsy(); - }); - - it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getHealthLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/health/health-report.tsx b/packages/react-components/lib/reports/health/health-report.tsx deleted file mode 100644 index 3ea2142f0..000000000 --- a/packages/react-components/lib/reports/health/health-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { HealthReportTable, HealthRowsType } from './health-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface HealthReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const HealthReport = (props: HealthReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default HealthReport; diff --git a/packages/react-components/lib/reports/health/index.ts b/packages/react-components/lib/reports/health/index.ts deleted file mode 100644 index bfbe0ebbe..000000000 --- a/packages/react-components/lib/reports/health/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './health-report'; -export * from './health-report-table'; diff --git a/packages/react-components/lib/reports/index.ts b/packages/react-components/lib/reports/index.ts deleted file mode 100644 index 752b658bd..000000000 --- a/packages/react-components/lib/reports/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './log-management'; -export * from './multi-level-menu'; -export * from './default-dates-form'; -export * from './default-report-interface'; -export * from './dispenser-state'; -export * from './door-state'; -export * from './fleet-state'; -export * from './health'; -export * from './ingestor-state'; -export * from './lift-state'; -export * from './user-report'; -export * from './task-summary'; -export * from './utils'; diff --git a/packages/react-components/lib/reports/ingestor-state/index.ts b/packages/react-components/lib/reports/ingestor-state/index.ts deleted file mode 100644 index a899427b2..000000000 --- a/packages/react-components/lib/reports/ingestor-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ingestor-state-report'; -export * from './ingestor-state-report-table'; diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx deleted file mode 100644 index 0ac83589b..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getIngestorLogs } from '../utils.spec'; -import { IngestorStateReportTable } from './ingestor-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Ingestor table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('ingestor-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Guid')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx deleted file mode 100644 index 565b40582..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type IngestorStateRowsType = { - created: string; //date - guid: string; - state: string; -}[]; - -export interface IngestorStateReportTable extends DefaultLogTableProps { - rows: IngestorStateRowsType | []; -} - -export const IngestorStateReportTable = (props: IngestorStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.guid} - columns={[ - { - headerName: 'Guid', - field: 'guid', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.guid}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx deleted file mode 100644 index 72b99981a..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getIngestorLogs, reportConfigProps } from '../utils.spec'; -import { IngestorStateReport } from './ingestor-state-report'; - -describe('Ingestor State report', () => { - const getLogsPromise = async () => getIngestorLogs(); - - it('smoke test', async () => { - await waitFor(() => { - render(); - }); - }); - - it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Ingestor State')).toBeFalsy(); - }); - - it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getIngestorLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx deleted file mode 100644 index a85c0192e..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { IngestorStateReportTable, IngestorStateRowsType } from './ingestor-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface IngestorStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const IngestorStateReport = (props: IngestorStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default IngestorStateReport; diff --git a/packages/react-components/lib/reports/lift-state/index.ts b/packages/react-components/lib/reports/lift-state/index.ts deleted file mode 100644 index aea8be4ac..000000000 --- a/packages/react-components/lib/reports/lift-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './lift-state-report'; -export * from './lift-state-report-table'; diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx deleted file mode 100644 index 704d3555e..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getLiftLogs } from '../utils.spec'; -import { LiftStateReportTable } from './lift-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Lift table test', () => { - let mockAddMoreRows: ReturnType; - let root: RenderResult; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('lift-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - // -3. from the tr of the table header, filter and pagination table - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Door State')).toBeTruthy(); - expect(screen.queryByText('Destination Floor')).toBeTruthy(); - expect(screen.queryByText('Motion State')).toBeTruthy(); - expect(screen.queryByText('Current Floor')).toBeTruthy(); - expect(screen.queryByText('Session ID')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx deleted file mode 100644 index f10c810ad..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type LiftStateRowsType = { - created: string; //date - state: string; - lift: { id: number; name: string }; - door_state: string; - destination_floor: string; - motion_state: string; - current_floor: string; - session_id: string; -}[]; - -export interface LiftStateReportTable extends DefaultLogTableProps { - rows: LiftStateRowsType | []; -} - -export const LiftStateReportTable = (props: LiftStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.lift.id} - columns={[ - { - headerName: 'Session ID', - field: 'session_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.session_id}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Door State', - field: 'door_state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.door_state}; - }, - }, - { - headerName: 'Destination Floor', - field: 'destination_floor', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.destination_floor}; - }, - }, - { - headerName: 'Motion State', - field: 'motion_state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.motion_state}; - }, - }, - { - headerName: 'Current Floor', - field: 'current_floor', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.current_floor}; - }, - }, - - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx deleted file mode 100644 index 4238531ad..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getLiftLogs, reportConfigProps } from '../utils.spec'; -import { LiftStateReport } from './lift-state-report'; - -describe('Lift State report', () => { - const getLogsPromise = async () => getLiftLogs(); - - it('smoke test', async () => { - act(() => { - render(); - }); - await waitFor(() => { - render(); - }); - }); - - it('doesn`t shows the table when logs list is empty', async () => { - act(() => { - render( []} />); - }); - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Lift State')).toBeFalsy(); - }); - - it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getLiftLogs(); - }; - await act(async () => { - render(, { - wrapper: TestLocalizationProvider, - }); - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - fireEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report.tsx deleted file mode 100644 index b7ae86377..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { LiftStateReportTable, LiftStateRowsType } from './lift-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface LiftStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const LiftStateReport = (props: LiftStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default LiftStateReport; diff --git a/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx b/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx deleted file mode 100644 index 5e6f7454a..000000000 --- a/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { cleanup, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { CustomLookupFilter } from './custom-lookup-filter'; - -describe('Custom Lookup filter', () => { - let mockOnChange: ReturnType; - beforeEach(() => { - mockOnChange = jasmine.createSpy(); - }); - - afterEach(() => { - cleanup(); - }); - - it('triggers a new search closing the filter selector', () => { - render( - , - ); - - // click on existing selected filter "error" to open up input to expose "warn" - - // TODO [CR]: FIX ERROR - // Error: Expected spy unknown to have been called. - // TestingLibraryElementError: Unable to find an element with the text: warn. This could be because the text is broken up by multiple elements. - // In this case, you can provide a function for your text matcher to make your matcher more flexible. - userEvent.click(screen.getByText('error')); - // userEvent.click(screen.getByText('warn')); - // userEvent.type(screen.getByText('warn'), '{esc}'); - // expect(mockOnChange).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx b/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx deleted file mode 100644 index c4c8af59b..000000000 --- a/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { - Checkbox, - FormControl, - Input, - InputLabel, - ListItemText, - MenuItem, - Select, -} from '@mui/material'; -import React from 'react'; - -const ITEM_HEIGHT = 48; -const ITEM_PADDING_TOP = 8; -const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 7 + ITEM_PADDING_TOP, - width: 250, - }, - }, -}; - -export interface CustomLookupFilterProps { - lookup: Record; - selectedFilter: string[]; - onFilterChange: React.Dispatch>; -} - -export const CustomLookupFilter = (props: CustomLookupFilterProps): React.ReactElement => { - // FIXME - select closes every time a filter param is selected, which is bad for ux - const { lookup, selectedFilter, onFilterChange } = props; - return ( - - Level Filter - } - renderValue={(selecteds) => - (selecteds as string[]).map((selected: string) => lookup[selected]).join(', ') - } - MenuProps={MenuProps} - style={{ marginTop: 0 }} - > - {Object.keys(lookup).map((key: string) => ( - - -1} /> - - - ))} - - - ); -}; diff --git a/packages/react-components/lib/reports/log-management/index.ts b/packages/react-components/lib/reports/log-management/index.ts deleted file mode 100644 index ca7102e01..000000000 --- a/packages/react-components/lib/reports/log-management/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './log-table'; -export * from './search-log-form'; -export * from './log-level'; -export * from './log-management'; diff --git a/packages/react-components/lib/reports/log-management/log-level.ts b/packages/react-components/lib/reports/log-management/log-level.ts deleted file mode 100644 index e1e7679da..000000000 --- a/packages/react-components/lib/reports/log-management/log-level.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Order by severity - */ - -export enum LogLevel { - Fatal = 'fatal', - Error = 'error', - Warn = 'warn', - Info = 'info', - Debug = 'debug', - Trace = 'trace', - All = 'all', -} diff --git a/packages/react-components/lib/reports/log-management/log-management.spec.tsx b/packages/react-components/lib/reports/log-management/log-management.spec.tsx deleted file mode 100644 index 1f6581739..000000000 --- a/packages/react-components/lib/reports/log-management/log-management.spec.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { render, waitFor } from '@testing-library/react'; -import React from 'react'; -import { LogManagement } from './log-management'; -import { LogRowsType } from './log-table'; -import { TestLocalizationProvider } from '../../test/locale'; - -const originalError = console.error; -beforeAll(() => { - // this is just a little hack to silence - // Warning: An update to ComponentName inside a test was not wrapped in act(...). - console.error = (...args) => { - if (/Warning.*not wrapped in act/.test(args[0])) { - return; - } - originalError.call(console, ...args); - }; -}); - -afterAll(() => { - console.error = originalError; -}); - -const getLogLabels = () => [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, -]; - -const getLogs = () => { - const rows: LogRowsType = []; - for (let i = 0; i < 200; i++) { - rows.push({ - message: 'Test' + i, - level: 'WARN', - created: new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(), - container: { id: i, name: 'container' }, - }); - } - return rows; -}; - -const getLogsPromise = async () => await getLogs(); -const getLabelsPromise = async () => await getLogLabels(); - -it('smoke test', async () => { - // Added the waitFor because this component is updating a state inside a useEffect. - await waitFor(() => { - render(, { - wrapper: TestLocalizationProvider, - }); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/log-management.tsx b/packages/react-components/lib/reports/log-management/log-management.tsx deleted file mode 100644 index e139f1f74..000000000 --- a/packages/react-components/lib/reports/log-management/log-management.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material'; -import { SearchLogForm } from './search-log-form'; -import { LogRowsType, LogTable } from './log-table'; - -const classes = { - table: 'log-management-table', - background: 'log-management-background', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.background}`]: { - backgroundColor: theme.palette.background.paper, - }, - [`& .${classes.table}`]: { - overflowY: 'scroll', - paddingTop: '20px', - }, -})); - -export interface LogQueryPayload { - toLogDate?: unknown | null; - fromLogDate?: unknown | null; - logLabel?: string | null; - logLevel?: string | null; - offset?: number | null; -} - -export interface LogManagementProps { - getLogs: (data: LogQueryPayload) => Promise; - getLabels: () => Promise<{ label: string; value: string }[]>; -} - -export const LogManagement = (props: LogManagementProps): React.ReactElement => { - const { getLogs, getLabels } = props; - const [logs, setLogs] = React.useState([]); - const [logLabels, setLogLabels] = React.useState<{ label: string; value: string }[]>([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - React.useEffect(() => { - const getLogLabels = async () => { - const labels = await getLabels(); - setLogLabels(labels); - }; - getLogLabels(); - }, [getLabels]); - - const searchLogs = async (payload: LogQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && } -
-
- ); -}; diff --git a/packages/react-components/lib/reports/log-management/log-table.spec.tsx b/packages/react-components/lib/reports/log-management/log-table.spec.tsx deleted file mode 100644 index d0f1ea1aa..000000000 --- a/packages/react-components/lib/reports/log-management/log-table.spec.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { render, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { LogLevel } from './log-level'; -import { LogRowsType, LogTable } from './log-table'; - -const rows = [] as LogRowsType; - -const logLevels = Object.values(LogLevel); - -const getRandomLogLevel = () => { - const number = Math.floor(Math.random() * logLevels.length); - return logLevels[number]; -}; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -for (let i = 0; i < 110; i++) { - rows.push({ - message: 'Test' + i, - level: getRandomLogLevel().toUpperCase(), - created: format(timestamp, 'MMM dd yyyy hh:mm aaa'), - container: { id: i, name: 'container' }, - }); -} - -describe('Log table test', () => { - it('formats dates correctly', async () => { - const { getAllByTestId } = render(); - const tableFirstDateElement = (await getAllByTestId('log-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const { container } = render(); - const allRows = container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); -}); - -describe('Table footer Pagination', () => { - it('show the correct number of rows per page', () => { - render(); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('1–100 of 110')).toBeTruthy(); - }); - - it('can change the rows per page', async () => { - // FIXME[CR]: - // ERROR: - // Unable to find an element with the text: 100. - // This could be because the text is broken up by multiple elements. - // In this case, you can provide a function for your text matcher to make your matcher more flexible. - // fireEvent.click(screen.getByText('100')); - // fireEvent.click(screen.getByText('50')); - // // NOTE: mui v5 is using the unicode char '–', different from '-'!! - // expect(screen.getByText('1–50 of 110')).toBeTruthy(); - }); - - it('advance page when the `Next Page` button is clicked ', async () => { - render(); - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('101–110 of 110')).toBeTruthy(); - }); - - it('goes to previous page when the `Previous page` button is clicked ', () => { - render(); - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('101–110 of 110')).toBeTruthy(); - - const previousPageButton = screen.queryByLabelText('Go to previous page'); - previousPageButton && fireEvent.click(previousPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('1–100 of 110')).toBeTruthy(); - }); -}); - -describe('Applies styles to labels correctly', () => { - const styleRows = [] as LogRowsType; - - for (let i = 0; i < logLevels.length; i++) { - styleRows.push({ - message: 'Test' + i, - level: logLevels[i].toUpperCase(), - created: format(timestamp, 'MMM dd yyyy hh:mm aaa'), - container: { id: i, name: 'container' }, - }); - } - - beforeEach(() => { - render(); - }); - - it('set the style correctly when the label ERROR ', () => { - expect(screen.getByText('ERROR').className).toContain('log-table-error'); - }); - - it('set the style correctly when the label DEBUG ', () => { - expect(screen.getByText('DEBUG').className).toContain('log-table-debug'); - }); - - it('set the style correctly when the label WARN ', () => { - expect(screen.getByText('WARN').className).toContain('log-table-warn'); - }); - - it('set the style correctly when the label FATAL ', () => { - expect(screen.getByText('FATAL').className).toContain('log-table-error'); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/log-table.tsx b/packages/react-components/lib/reports/log-management/log-table.tsx deleted file mode 100644 index 54b4ecc42..000000000 --- a/packages/react-components/lib/reports/log-management/log-table.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { LogLevel } from '.'; -import { Typography, styled } from '@mui/material'; -import { format } from 'date-fns'; -import { CustomLookupFilter } from './custom-lookup-filter'; - -export type ContainerType = { - id: number; - name: string; -}; - -export type LogRowsType = { - level: string; - message: string; - created: string; - container: ContainerType; -}[]; - -export interface LogTableProps { - rows: LogRowsType | []; - addMoreRows?(): void; -} - -const classes = { - error: 'log-table-error', - debug: 'log-table-debug', - warn: 'log-table-warn', - info: 'log-table-info', - cellContent: 'log-table-cell-content', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`& .${classes.error}`]: { - color: theme.palette.error.main, - }, - [`& .${classes.debug}`]: { - color: theme.palette.secondary.dark, - }, - [`& .${classes.warn}`]: { - color: theme.palette.warning.light, - }, - [`& .${classes.info}`]: { - color: theme.palette.info.main, - }, - [`& .${classes.cellContent}`]: { - display: 'block', - marginBlockStart: '1em', - marginBlockEnd: '1em', - marginInlineStart: '0px', - marginInlineEnd: '0px', - }, -})); - -export const LogTable = (props: LogTableProps): React.ReactElement => { - const { rows, addMoreRows } = props; - const [pageSize, setPageSize] = React.useState(100); - const getLogLevelStyle = (level: string): string | undefined => { - level = level.toLowerCase(); - switch (level) { - case LogLevel.Error: - return classes.error; - case LogLevel.Warn: - return classes.warn; - case LogLevel.Fatal: - return classes.error; - case LogLevel.Debug: - return classes.debug; - case LogLevel.Info: - return classes.info; - default: - return undefined; - } - }; - - const [selectedFilter, setSelectedFilter] = React.useState([]); - // FIXME: we cannot copy the LogLevel Enum directly and remove the all attribute because it has a protected attribute. - const logLevels = React.useMemo(() => { - const logLevelCopy: Record = {}; - Object.keys(LogLevel).forEach((element: string) => { - logLevelCopy[element] = LogLevel[element as keyof typeof LogLevel]; - }); - delete logLevelCopy['All']; - return logLevelCopy; - }, []); - - return ( - - r.container.id} - autoHeight={true} - columns={[ - { - headerName: 'Level', - field: 'level', - type: 'string', - align: 'center', - width: 100, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {rowData.row.level} - - ); - }, - }, - { - headerName: 'Container', - field: 'name', - type: 'string', - align: 'center', - width: 120, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {rowData.row.container.name ? rowData.row.container.name : 'Unknown'} - - ); - }, - }, - { - headerName: 'Message', - field: 'message', - type: 'string', - width: 1200, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.value}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - sortable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - components={{ - Toolbar: () => ( - - ), - }} - rows={rows.filter((row) => { - if (!selectedFilter.includes(row.level)) return row; - })} - pageSize={pageSize} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - onPageSizeChange={(pageSize) => { - setPageSize(pageSize); - }} - disableColumnMenu={true} - /> - - ); -}; diff --git a/packages/react-components/lib/reports/log-management/logging.stories.tsx b/packages/react-components/lib/reports/log-management/logging.stories.tsx deleted file mode 100644 index c34541cf0..000000000 --- a/packages/react-components/lib/reports/log-management/logging.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { LogManagement } from './log-management'; -import { LogTable } from './log-table'; -import { SearchLogForm } from './search-log-form'; - -export default { - title: 'Logging', - argTypes: {}, -} as Meta; - -const getLogLabels = () => [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, -]; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(); - -function randomDate(start: Date, end: Date) { - return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); -} - -const getLogs = () => { - const rows = []; - for (let i = 0; i < 500; i++) { - rows.push({ - message: 'Test' + i, - level: 'Debug', - created: randomDate(new Date(2012, 0, 1), new Date()).toISOString(), - container: { id: i + 2, name: 'container_test' }, - }); - } - return rows; -}; - -const getLogsPromise = async () => getLogs(); -const getLabelsPromise = async () => getLogLabels(); - -export const LogManagementExample: Story = (args) => ( - -); - -export const SimpleSearchLogForm: Story = (args) => ( - -); - -export const SimpleLogTable: Story = (args) => { - const logs = getLogs(); - logs.unshift({ - message: ` - npm ERR! code ELIFECYCLE - npm ERR! errno 1 - npm ERR! react-components@0.0.1 build: 'npm run lint && tsc --build' - npm ERR! Exit status 1 - npm ERR! - npm ERR! Failed at the react-components@0.0.1 build script. - npm ERR! This is probably not a problem with npm. There is likely additional logging output above. - - npm ERR! A complete log of this run can be found in: - npm ERR! /home/ekumen/.npm/_logs/2021-01-18T21_20_07_480Z-debug.log`, - level: 'Error', - created: timestamp, - container: { id: 1, name: 'container_test' }, - }); - logs.unshift({ - message: `long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long msg`, - level: 'Debug', - created: timestamp, - container: { id: 0, name: 'container_test' }, - }); - - return ; -}; diff --git a/packages/react-components/lib/reports/log-management/search-filter.tsx b/packages/react-components/lib/reports/log-management/search-filter.tsx deleted file mode 100644 index d056f0eec..000000000 --- a/packages/react-components/lib/reports/log-management/search-filter.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { - FormControl, - InputLabel, - MenuItem, - Select, - SelectChangeEvent, - styled, -} from '@mui/material'; - -const classes = { - formControl: 'search-filter-formcontrol', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`& .${classes.formControl}`]: { - margin: theme.spacing(1), - minWidth: '230px', - }, -})); - -interface SearchFilterProps { - options: { label: string; value: string }[]; - handleOnChange: (event: SelectChangeEvent, child: React.ReactNode) => void; - label: string; - name: string; - currentValue: string | number; -} - -export const SearchFilter = (props: SearchFilterProps): React.ReactElement => { - const { handleOnChange, options, name, label, currentValue } = props; - - return ( - -
- - {label} - - -
-
- ); -}; diff --git a/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx b/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx deleted file mode 100644 index 483468a2d..000000000 --- a/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { SearchLogForm } from './search-log-form'; - -describe('Search log form tests', () => { - const logLabel = [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, - ]; - - it('smoke test', () => { - render(, { - wrapper: TestLocalizationProvider, - }); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/search-log-form.tsx b/packages/react-components/lib/reports/log-management/search-log-form.tsx deleted file mode 100644 index dab531994..000000000 --- a/packages/react-components/lib/reports/log-management/search-log-form.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { SelectChangeEvent, styled, TextField } from '@mui/material'; -import Button from '@mui/material/Button'; -import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import React from 'react'; -import { LogQueryPayload } from '.'; -import { LogLevel } from './log-level'; -import { SearchFilter } from './search-filter'; - -type DateTimePickerProps = React.ComponentProps; - -interface SearchLogFormProps { - logLabelValues: { label: string; value: string }[]; - search?: (payload: LogQueryPayload) => void; -} - -const classes = { - searchForm: 'search-log-search-from', - searchButton: 'search-log-search-button', - formControl: 'search-log-form-form-control', - background: 'search-log-form-background', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.background}`]: { - backgroundColor: theme.palette.background.paper, - }, - [`& .${classes.searchForm}`]: { - display: 'grid', - gridTemplateColumns: '1fr 1fr 1fr 1fr', - alignItems: 'center', - justifyItems: 'center', - }, - [`& .${classes.searchButton}`]: { - width: '100%', - }, - [`& .${classes.formControl}`]: { - margin: theme.spacing(1), - minWidth: 120, - }, -})); - -const logLevelValues = [ - { label: 'ALL', value: LogLevel.All }, - { label: 'FATAL', value: LogLevel.Fatal }, - { label: 'ERROR', value: LogLevel.Error }, - { label: 'WARN', value: LogLevel.Warn }, - { label: 'INFO', value: LogLevel.Info }, - { label: 'DEBUG', value: LogLevel.Debug }, -]; - -export const SearchLogForm = (props: SearchLogFormProps): React.ReactElement => { - const { search, logLabelValues } = props; - // The log contains information from different services, the label help us differentiate the service - const [logLabel, setLogLabel] = React.useState(''); - const [logLevel, setLogLevel] = React.useState(LogLevel.All); - const [fromLogDate, setFromLogDate] = React.useState(new Date()); - const [toLogDate, setToLogDate] = React.useState(new Date()); - - const searchQuery = () => { - search && search({ toLogDate, fromLogDate, logLabel, logLevel }); - }; - - const handleLogLabelChange = React.useCallback((event: SelectChangeEvent) => { - setLogLabel(event.target.value as string); - }, []); - - const handleLogLevelChange = React.useCallback((event: SelectChangeEvent) => { - setLogLevel(event.target.value as LogLevel); - }, []); - - const handleFromLogDateChange: Required['onChange'] = React.useCallback( - (date) => { - setFromLogDate(date); - }, - [], - ); - - const handleToLogDateChange: Required['onChange'] = React.useCallback( - (date) => { - setToLogDate(date); - }, - [], - ); - - return ( - -
- - - - - } - /> - } - /> -
- -

- -
- ); -}; diff --git a/packages/react-components/lib/reports/multi-level-menu.spec.tsx b/packages/react-components/lib/reports/multi-level-menu.spec.tsx deleted file mode 100644 index 9e8c77bb1..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.spec.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { cleanup, render, screen, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { ExpandableMultilevelMenuProps, MultiLevelMenu } from './multi-level-menu'; - -describe('Multi level menu', () => { - let buildMenuStructure: ExpandableMultilevelMenuProps[]; - let mockClickAllLogs: ReturnType; - let mockClickRobotStates: ReturnType; - - beforeEach(() => { - mockClickAllLogs = jasmine.createSpy(); - mockClickRobotStates = jasmine.createSpy(); - - const buildReportMenuStructure = (): ExpandableMultilevelMenuProps[] => { - return [ - { - icon:

Mock Icon 1

, - title: 'All logs', - items: [], - onClick: mockClickAllLogs, - }, - { - icon:

Mock Icon 2

, - title: 'Robots', - items: [ - { - title: 'Robot states', - items: [], - onClick: mockClickRobotStates, - }, - ], - }, - ] as ExpandableMultilevelMenuProps[]; - }; - buildMenuStructure = buildReportMenuStructure(); - }); - - afterEach(() => { - cleanup(); - }); - - it('renders correctly with `All logs` and `Robots` titles on the first level', () => { - render(); - expect(screen.getByText('Mock Icon 1')).toBeTruthy(); - expect(screen.getByText('All logs')).toBeTruthy(); - - expect(screen.getByText('Mock Icon 2')).toBeTruthy(); - expect(screen.getByText('Robots')).toBeTruthy(); - - expect(screen.queryByText('Robot states')).toBeFalsy(); - }); - - it('triggers an action, if defined, when clicking on a title ', () => { - render(); - fireEvent.click(screen.getByText('All logs')); - expect(mockClickAllLogs).toHaveBeenCalledTimes(1); - }); - - it('shows the child level after clicking the parent level', () => { - render(); - fireEvent.click(screen.getByText('Robots')); - expect(screen.getByText('Robot states')).toBeTruthy(); - }); - - it('triggers an action, if defined, after clicking on a child level title', () => { - render(); - fireEvent.click(screen.getByText('Robots')); - fireEvent.click(screen.getByText('Robot states')); - expect(mockClickRobotStates).toHaveBeenCalledTimes(1); - }); - - it('shows the parent level and the child level titles simultaneously', () => { - render(); - fireEvent.click(screen.getByText('Robots')); - screen.getByText('Robot states'); - expect(screen.getByText('Robots')).toBeTruthy(); - expect(screen.getByText('Robot states')).toBeTruthy(); - }); -}); diff --git a/packages/react-components/lib/reports/multi-level-menu.stories.tsx b/packages/react-components/lib/reports/multi-level-menu.stories.tsx deleted file mode 100644 index 21dab7618..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.stories.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import AndroidIcon from '@mui/icons-material/Android'; -import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; -import { styled } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { ExpandableMultilevelMenuProps, MultiLevelMenu } from './multi-level-menu'; - -const classes = { - container: 'mlm-story-container', -}; - -const MlmStory = styled('div')(({ theme }) => ({ - [`&.${classes.container}`]: { - backgroundColor: theme.palette.background.paper, - }, -})); - -export default { - title: 'Multi level menu', -} as Meta; - -const menuStructure = [ - { - icon: , - title: 'All logs', - items: [], - }, - { - icon: , - title: 'Robots', - items: [ - { - title: 'Robot states', - items: [], - }, - ], - }, - { - icon: , - title: 'Test', - items: [ - { - title: 'Test-child', - items: [], - }, - ], - }, -] as ExpandableMultilevelMenuProps[]; - -export const MultiLevelMenuStory: Story = (args) => ( - - - -); diff --git a/packages/react-components/lib/reports/multi-level-menu.tsx b/packages/react-components/lib/reports/multi-level-menu.tsx deleted file mode 100644 index 8ade25fbe..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState } from 'react'; -import { styled, ListProps } from '@mui/material'; -import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import Collapse from '@mui/material/Collapse'; -import ExpandLess from '@mui/icons-material/ExpandLess'; -import ExpandMore from '@mui/icons-material/ExpandMore'; - -const classes = { - textAndIcon: 'mlm-text-and-icon', -}; - -const StyledList = styled((props: ListProps) => )(({ theme }) => ({ - [`& .${classes.textAndIcon}`]: { - color: theme.palette.text.primary, - }, -})); - -interface ListItemBodyProps { - icon?: JSX.Element; - title: string; -} - -const ListItemBody = (props: ListItemBodyProps): JSX.Element => { - return ( - <> - {props.icon} - - - ); -}; - -interface MenuItemProps { - icon?: JSX.Element; - title: string; - items?: { - title: string; - to: string; - }[]; - onClick?: () => void; -} - -const MenuItem = React.memo((props: MenuItemProps): JSX.Element => { - return ( - - - - ); -}); - -export interface ExpandableMultilevelMenuProps { - icon?: JSX.Element; - title: string; - items: MenuItemProps[]; - onClick?: () => void; -} - -const ExpandableMenuItem = (props: ExpandableMultilevelMenuProps): JSX.Element => { - const { items, icon, title } = props; - const [open, setOpen] = useState(false); - const handleClick = () => { - setOpen(!open); - }; - - return ( -
- - - {open ? : } - - - - -
- ); -}; - -export interface MultilevelMenuProps { - menuStructure: (ExpandableMultilevelMenuProps | MenuItemProps)[]; -} - -export const MultiLevelMenu = React.memo((props: MultilevelMenuProps): React.ReactElement => { - const createList = (items: (ExpandableMultilevelMenuProps | MenuItemProps)[]) => { - const menu: JSX.Element[] = []; - items.map((menuItem: ExpandableMultilevelMenuProps | MenuItemProps) => { - // If it has children's - if (Array.isArray(menuItem.items) && menuItem.items.length > 0) { - menu.push( - , - ); - } else { - menu.push( - , - ); - } - }); - return menu.concat(); - }; - - return {createList(props.menuStructure)}; -}); diff --git a/packages/react-components/lib/reports/task-summary/index.ts b/packages/react-components/lib/reports/task-summary/index.ts deleted file mode 100644 index ce0aeaab6..000000000 --- a/packages/react-components/lib/reports/task-summary/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './task-summary-report'; -export * from './task-summary-report-table'; diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx deleted file mode 100644 index 10c657911..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { cleanup, render, RenderResult, screen, fireEvent } from '@testing-library/react'; -import { format } from 'date-fns'; -import React from 'react'; -import { getTaskSummaryLogs } from '../utils.spec'; -import { TaskSummaryReportTable } from './task-summary-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Task Summary Table Test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('task-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows the correct headers', () => { - expect(screen.queryByText('Task ID')).toBeTruthy(); - expect(screen.queryByText('Fleet')).toBeTruthy(); - expect(screen.queryByText('Robot')).toBeTruthy(); - expect(screen.queryByText('Task Description')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Time')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows function', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && fireEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx deleted file mode 100644 index bd64e9316..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import type { Time } from 'api-client'; -import { format } from 'date-fns'; -import { Typography } from '@mui/material'; -import { rosTimeToJs } from '../../utils'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { returnTaskDetails } from './utils'; - -export type TaskSummaryRowsType = { - created: string; //date - fleet: { id: number; name: string }; - robot: { id: number; name: string; model?: string }; - task_id: string; - task_profile: unknown; - state: string; - status: string; - submission_time: Time; - start_time: Time; - end_time: Time; -}[]; - -export interface TaskSummaryReportTable extends DefaultLogTableProps { - rows: TaskSummaryRowsType | []; -} - -export const TaskSummaryReportTable = (props: TaskSummaryReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.task_id} - columns={[ - { - headerName: 'Task ID', - field: 'task_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.task_id}; - }, - }, - { - headerName: 'Fleet', - field: 'fleet_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.fleet.name}; - }, - }, - { - headerName: 'Robot', - field: 'robot_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.name}; - }, - }, - { - headerName: 'Task Description', - field: 'description', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - const taskTypeDetails = returnTaskDetails( - rowData.row.task_id, - rowData.row.task_profile.description, - ); - return taskTypeDetails; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Time', - field: 'time_information', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - const submissionTime = rosTimeToJs( - rowData.row.task_profile.submission_time, - ).toLocaleTimeString(); - const startTime = rosTimeToJs(rowData.row.start_time).toLocaleTimeString(); - const endTime = rosTimeToJs(rowData.row.end_time).toLocaleTimeString(); - return ( - <> - Submitted: {submissionTime} - Start: {startTime} - End: {endTime} - - ); - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx deleted file mode 100644 index 60ce106e7..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getTaskSummaryLogs, reportConfigProps } from '../utils.spec'; -import { TaskSummaryReport } from './task-summary-report'; - -const getLogsPromise = async () => getTaskSummaryLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('does not show the table when the logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Task Summary')).toBeFalsy(); -}); - -it('calls the Retrieve Logs function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getTaskSummaryLogs(); - }; - - render(, { - wrapper: TestLocalizationProvider, - }); - const retrieveLogsButton = screen.getByRole('button', { name: /Retrieve Logs/i }); - expect(retrieveLogsButton).toBeTruthy(); - fireEvent.click(retrieveLogsButton); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report.tsx deleted file mode 100644 index f7d0c2835..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { TaskSummaryReportTable, TaskSummaryRowsType } from './task-summary-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface TaskSummaryReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const TaskSummaryReport = (props: TaskSummaryReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default TaskSummaryReport; diff --git a/packages/react-components/lib/reports/task-summary/utils.tsx b/packages/react-components/lib/reports/task-summary/utils.tsx deleted file mode 100644 index 2d0bb4851..000000000 --- a/packages/react-components/lib/reports/task-summary/utils.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Typography } from '@mui/material'; -// import type { TaskDescription } from 'api-client'; -import React from 'react'; - -// FIXME: no `any` -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -export const returnTaskDetails = (taskId: string, taskDescription: any): React.ReactNode => { - let taskTypeDetails; - if (taskId.includes('Loop')) { - taskTypeDetails = taskDescription.loop; - return ( - <> - Num of Loops: {taskTypeDetails.num_loops} - Start Point: {taskTypeDetails.start_name} - End Point: {taskTypeDetails.finish_name} - - ); - } else if (taskId.includes('Delivery')) { - taskTypeDetails = taskDescription.delivery; - return ( - <> - Pick Up: {taskTypeDetails.pickup_place_name} - Drop Off: {taskTypeDetails.dropoff_place_name} - End Point: {taskTypeDetails.items} - - ); - } else if (taskId.includes('Clean')) { - taskTypeDetails = taskDescription.clean; - return ( - <> - Start Point: {taskTypeDetails.start_waypoint} - - ); - } -}; diff --git a/packages/react-components/lib/reports/user-report/index.ts b/packages/react-components/lib/reports/user-report/index.ts deleted file mode 100644 index 9c04239c0..000000000 --- a/packages/react-components/lib/reports/user-report/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './user-login-failure-report-table'; -export * from './user-login-failure-report'; -export * from './user-login-report'; -export * from './user-login-report-table'; -export * from './user-logout-report'; -export * from './user-logout-report-table'; diff --git a/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx b/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx deleted file mode 100644 index c8198dfb9..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLoginFailureRowsType = { - created: string; //date - ip_address: string; - client_id: string; - username: string; - error: string; -}[]; - -export interface UserLoginFailureReportTable extends DefaultLogTableProps { - rows: UserLoginFailureRowsType; -} - -export const UserLoginFailureReportTable = ( - props: UserLoginFailureReportTable, -): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.client_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - - { - headerName: 'Client ID', - field: 'client_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.client_id}; - }, - }, - { - headerName: 'IP Addr.', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx b/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx deleted file mode 100644 index d40ee77af..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - DefaultReportContainer, - defaultReportClasses, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { - UserLoginFailureReportTable, - UserLoginFailureRowsType, -} from './user-login-failure-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface UserLoginFailureReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLoginFailureReport = (props: UserLoginFailureReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLoginFailureReport; diff --git a/packages/react-components/lib/reports/user-report/user-login-report-table.tsx b/packages/react-components/lib/reports/user-report/user-login-report-table.tsx deleted file mode 100644 index b1a95ba53..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-report-table.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLoginRowsType = { - client_id: string; - created: string; //date - ip_address: string; - user_id: string; - username: string; -}[]; - -export interface UserLoginReportTable extends DefaultLogTableProps { - rows: UserLoginRowsType; -} - -export const UserLoginReportTable = (props: UserLoginReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.client_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - { - headerName: 'Client ID', - field: 'client_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.client_id}; - }, - }, - { - headerName: 'User ID', - field: 'user_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.user_id}; - }, - }, - { - headerName: 'IP Addr', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-login-report.tsx b/packages/react-components/lib/reports/user-report/user-login-report.tsx deleted file mode 100644 index 630fa09dc..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { UserLoginReportTable, UserLoginRowsType } from './user-login-report-table'; - -import { ReportConfigProps } from '../utils'; -export interface UserLoginReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLoginReport = (props: UserLoginReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLoginReport; diff --git a/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx b/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx deleted file mode 100644 index cc96e062f..000000000 --- a/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLogoutRowsType = { - created: string; //date - ip_address: string; - user_id: string; - username: string; -}[]; - -export interface UserLogoutReportTable extends DefaultLogTableProps { - rows: UserLogoutRowsType; -} - -export const UserLogoutReportTable = (props: UserLogoutReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.user_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - { - headerName: 'User ID', - field: 'user_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.user_id}; - }, - }, - { - headerName: 'IP Addr', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-logout-report.tsx b/packages/react-components/lib/reports/user-report/user-logout-report.tsx deleted file mode 100644 index 05198e7f9..000000000 --- a/packages/react-components/lib/reports/user-report/user-logout-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { UserLogoutReportTable, UserLogoutRowsType } from './user-logout-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface UserLogoutReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLogoutReport = (props: UserLogoutReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLogoutReport; diff --git a/packages/react-components/lib/reports/utils.spec.ts b/packages/react-components/lib/reports/utils.spec.ts deleted file mode 100644 index d3ac72ada..000000000 --- a/packages/react-components/lib/reports/utils.spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - DispenserStateRowsType, - DoorStateRowsType, - FleetStateRowsType, - HealthRowsType, - IngestorStateRowsType, - LiftStateRowsType, - TaskSummaryRowsType, -} from '.'; -import { ReportConfigProps } from './utils'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(); - -export const getDispenserLogs = (): DispenserStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - state: 'OPEN', - guid: `dispenser_test_${i}`, - created: timestamp, - }); - } - return rows; -}; - -export const getDoorLogs = (): DoorStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - state: 'OPEN', - door: { id: i, name: 'door_test' }, - }); - } - return rows; -}; - -export const getFleetLogs = (): FleetStateRowsType => { - const rows: FleetStateRowsType = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - fleet: { id: 1, name: 'fleet_test' }, - robot: { id: i, name: 'robot_test', model: 'model' }, - robot_battery_percent: 'test', - robot_location: 'test', - robot_mode: 'test', - robot_seq: 1, - robot_task_id: 'test', - }); - } - return rows as FleetStateRowsType; -}; - -export const getHealthLogs = (): HealthRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - device: { id: i, type: 'door', actor: 'door-1' }, - health_status: 'DEAD', - health_message: 'this is a message', - created: timestamp, - }); - } - return rows; -}; - -export const getIngestorLogs = (): IngestorStateRowsType => - getDispenserLogs() as IngestorStateRowsType; - -export const getLiftLogs = (): LiftStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - state: 'AVG', - door_state: 'Closed', - destination_floor: 'L1', - motion_state: 'DOWN', - current_floor: 'L2', - session_id: 'session', - created: timestamp, - lift: { id: i, name: `lift_${i}` }, - }); - } - return rows; -}; - -export const getTaskSummaryLogs = (): TaskSummaryRowsType => { - const exampleData = { - task_id: 'test', - submission_time: { sec: 131, nanosec: 553000000 }, - description: { - start_time: { sec: 1623383402, nanosec: 0 }, - priority: { value: 0 }, - task_type: { type: 1 }, - station: { task_id: '', robot_type: '', place_name: '' }, - loop: { task_id: '', robot_type: '', num_loops: 1, start_name: '', finish_name: '' }, - delivery: { - task_id: '1', - items: [], - pickup_place_name: '', - pickup_dispenser: '', - pickup_behavior: { name: '', parameters: [] }, - dropoff_place_name: '', - dropoff_ingestor: '', - dropoff_behavior: { name: '', parameters: [] }, - }, - clean: { start_waypoint: '' }, - }, - }; - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - fleet: { id: 1, name: 'fleet_test' }, - robot: { id: i, name: 'robot_test', model: 'model' }, - task_id: i.toString(), - task_profile: exampleData, - state: 'test', - status: 'test', - submission_time: { sec: 131, nanosec: 553000000 }, - start_time: { sec: 131, nanosec: 553000000 }, - end_time: { sec: 131, nanosec: 553000000 }, - }); - } - return rows; -}; - -export const reportConfigProps: ReportConfigProps = { - toLogDate: new Date(), - fromLogDate: new Date(), - onSelectFromDate: (/* date: Date */) => { - /* no-op */ - }, - onSelectToDate: (/* date: Date */) => { - /* no-op */ - }, -}; diff --git a/packages/react-components/lib/reports/utils.ts b/packages/react-components/lib/reports/utils.ts deleted file mode 100644 index 1defe236c..000000000 --- a/packages/react-components/lib/reports/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ReportConfigProps { - fromLogDate?: Date; - toLogDate?: Date; - onSelectFromDate?: (date: unknown) => void; - onSelectToDate?: (date: unknown) => void; -} diff --git a/packages/react-components/lib/stack-navigator.spec.ts b/packages/react-components/lib/stack-navigator.spec.ts index 705107ac1..1050be135 100644 --- a/packages/react-components/lib/stack-navigator.spec.ts +++ b/packages/react-components/lib/stack-navigator.spec.ts @@ -1,79 +1,57 @@ -import { renderHook, act } from '@testing-library/react'; -import { useStackNavigator } from '../lib'; +import { act, renderHook, RenderResult } from '@testing-library/react'; +import { StackNavigatorDispatch, useStackNavigator } from '../lib'; -describe('useStackNavigator', () => { - it('should initialize with the correct initial state', () => { - const initialState: number[] = [1, 2, 3]; - const homeView = 0; +let hookResult: RenderResult<[number[], StackNavigatorDispatch]>; +let stackDispatch: StackNavigatorDispatch; - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); +beforeEach(() => { + hookResult = renderHook(() => useStackNavigator([0], 0)).result; + stackDispatch = hookResult.current[1]; +}); - expect(result.current[0]).toEqual(initialState); +describe('useStackNavigator', () => { + it('push', () => { + act(() => stackDispatch.push(10)); + const stack = hookResult.current[0]; + expect(stack).toHaveSize(2); + expect(stack[0]).toBe(0); + expect(stack[1]).toBe(10); }); - it('should push a view onto the stack', () => { - const initialState: number[] = [1, 2]; - const homeView = 0; - - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); - + it('pop does not remove last item', () => { act(() => { - result.current[1].push(3); + stackDispatch.push(2); + stackDispatch.pop(); + stackDispatch.pop(); }); - - expect(result.current[0]).toEqual([1, 2, 3]); + const stack = hookResult.current[0]; + expect(stack).toHaveSize(1); + expect(stack[0]).toBe(0); }); - it('should pop a view from the stack', () => { - const initialState: number[] = [1, 2, 3]; - const homeView = 0; - - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); - + it('reset returns the stack to the initial state', () => { act(() => { - result.current[1].pop(); + stackDispatch.push(2); + stackDispatch.push(3); }); - - expect(result.current[0]).toEqual([1, 2]); - }); - - it('should not remove the last view when popping', () => { - const initialState: number[] = [1]; - const homeView = 0; - - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); - + expect(hookResult.current[0]).toHaveSize(3); act(() => { - result.current[1].pop(); + stackDispatch.reset(); }); - - expect(result.current[0]).toEqual([1]); + expect(hookResult.current[0]).toHaveSize(1); + expect(hookResult.current[0][0]).toBe(0); }); - it('should set the stack to the home view', () => { - const initialState: number[] = [1, 2]; - const homeView = 0; - - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); - + it('home pushes the home view onto the stack', () => { act(() => { - result.current[1].home(); + stackDispatch.push(2); + stackDispatch.push(3); }); - - expect(result.current[0]).toEqual([1, 2, 0]); - }); - - it('should reset the stack to the initial state', () => { - const initialState: number[] = [1, 2, 3]; - const homeView = 0; - - const { result } = renderHook(() => useStackNavigator(initialState, homeView)); - + expect(hookResult.current[0]).toHaveSize(3); act(() => { - result.current[1].push(4); - result.current[1].reset(); + stackDispatch.home(); }); - - expect(result.current[0]).toEqual(initialState); + expect(hookResult.current[0]).toHaveSize(4); + expect(hookResult.current[0][3]).toBe(0); }); });