diff --git a/cypress/e2e/manufacturer/manufacturer.cy.tsx b/cypress/e2e/manufacturer/manufacturer.cy.tsx index f22944ae4..2bf6bf157 100644 --- a/cypress/e2e/manufacturer/manufacturer.cy.tsx +++ b/cypress/e2e/manufacturer/manufacturer.cy.tsx @@ -9,6 +9,7 @@ describe('Manufacturer', () => { cy.findByText('Name').should('be.visible'); cy.findByText('URL').should('be.visible'); cy.findByText('Address').should('be.visible'); + cy.findByText('Telephone').should('be.visible'); }); it('should render manufacturer data', () => { @@ -18,11 +19,17 @@ describe('Manufacturer', () => { cy.findByText('Manufacturer B').should('be.visible'); cy.findByText('Manufacturer C').should('be.visible'); cy.findByText('http://example.com').should('be.visible'); + cy.findByText('http://test.com').should('be.visible'); cy.findByText('http://test.co.uk').should('be.visible'); - cy.findByText('http://123test.com').should('be.visible'); - cy.findByText('10 My Street').should('be.visible'); - cy.findByText('11 My Street').should('be.visible'); - cy.findByText('12 My Street').should('be.visible'); + cy.findByText('1 Example Street Oxford Oxfordshire OX1 2AB').should( + 'be.visible' + ); + cy.findByText('2 Example Street Oxford Oxfordshire OX1 2AB').should( + 'be.visible' + ); + cy.findByText('3 Example Street Oxford Oxfordshire OX1 2AB').should( + 'be.visible' + ); }); it('manufacturer url is correct and opens new webpage', () => { @@ -37,4 +44,105 @@ describe('Manufacturer', () => { .click(); cy.url().should('include', 'http://example.com'); }); + + it('adds a manufacturer with all fields', async () => { + cy.findByRole('button', { name: 'Add Manufacturer' }).click(); + cy.findByLabelText('Name *').type('Manufacturer D'); + cy.findByLabelText('URL').type('http://test.co.uk'); + cy.findByLabelText('Building number *').type('1'); + cy.findByLabelText('Street name *').type('Example Street'); + cy.findByLabelText('Town').type('Oxford'); + cy.findByLabelText('County').type('Oxfordshire'); + cy.findByLabelText('Post/Zip code *').type('OX1 2AB'); + cy.findByLabelText('Telephone number').type('07349612203'); + + cy.startSnoopingBrowserMockedRequest(); + + cy.findByRole('button', { name: 'Save' }).click(); + + cy.findBrowserMockedRequests({ + method: 'POST', + url: '/v1/manufacturers', + }).should((patchRequests) => { + expect(patchRequests.length).equal(1); + const request = patchRequests[0]; + expect(JSON.stringify(request.body)).equal( + '{"name":"Manufacturer D","url":"http://test.co.uk", "address": {building_number: "1", "street_name": "Example Street", "town": "Oxford", "county": "Oxfordshire", "postcode": "OX1 2AB",}, "telephone": "07349612203"}' + ); + }); + }); + + it('adds a manufacturer with only mandatory fields', async () => { + cy.findByRole('button', { name: 'Add Manufacturer' }).click(); + cy.findByLabelText('Name *').type('Manufacturer D'); + cy.findByLabelText('Building number *').type('1'); + cy.findByLabelText('Street name *').type('Example Street'); + cy.findByLabelText('Post/Zip code *').type('OX1 2AB'); + + cy.startSnoopingBrowserMockedRequest(); + + cy.findByRole('button', { name: 'Save' }).click(); + + cy.findBrowserMockedRequests({ + method: 'POST', + url: '/v1/manufacturers', + }).should((patchRequests) => { + expect(patchRequests.length).equal(1); + const request = patchRequests[0]; + expect(JSON.stringify(request.body)).equal( + '{"name":"Manufacturer D","url":"","address":{"building_number":"1","street_name":"Example Street","town":"","county":"","postcode":"OX1 2AB"},"telephone":""}' + ); + }); + + it('render error messages if fields are not filled', async () => { + cy.findByTestId('Add Manufacturer').click(); + cy.findByRole('button', { name: 'Save' }).click(); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('Please enter a name.'); + }); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('Please enter a building number.'); + }); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('Please enter a street name.'); + }); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('Please enter a post code or zip code.'); + }); + }); + it('displays error message when duplicate name entered', async () => { + cy.findByTestId('Add Manufacturer').click(); + cy.findByLabelText('Name *').type('Manufacturer A'); + cy.findByLabelText('Building number *').type('1'); + cy.findByLabelText('Street name *').type('Example Street'); + cy.findByLabelText('Post/Zip code *').type('OX1 2AB'); + + cy.findByRole('button', { name: 'Save' }).click(); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('A manufacturer with the same name already exists.'); + }); + }); + it('invalid url displays correct error message', async () => { + cy.findByTestId('Add Manufacturer').click(); + + cy.findByLabelText('URL').type('test.co.uk'); + + cy.findByRole('button', { name: 'Save' }).click(); + cy.findByRole('dialog') + .should('be.visible') + .within(() => { + cy.contains('Please enter a valid url.'); + }); + }); + }); }); diff --git a/src/api/manufacturer.tsx b/src/api/manufacturer.tsx index 6a0af49a8..33fadcfe5 100644 --- a/src/api/manufacturer.tsx +++ b/src/api/manufacturer.tsx @@ -1,8 +1,18 @@ import axios, { AxiosError } from 'axios'; -import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { + useMutation, + UseMutationResult, + useQuery, + useQueryClient, + UseQueryResult, +} from '@tanstack/react-query'; import { settings } from '../settings'; -import { Manufacturer } from '../app.types'; +import { + AddManufacturer, + AddManufacturerResponse, + Manufacturer, +} from '../app.types'; const getAllManufacturers = async (): Promise => { let apiUrl: string; @@ -13,7 +23,7 @@ const getAllManufacturers = async (): Promise => { } return axios - .get(`${apiUrl}/v1/manufacturer`, {}) + .get(`${apiUrl}/v1/manufacturers`, {}) .then((response) => response.data); }; @@ -33,3 +43,36 @@ export const useManufacturers = (): UseQueryResult< } ); }; + +const addManufacturer = async ( + manufacturer: AddManufacturer +): Promise => { + let apiUrl: string; + apiUrl = ''; + const settingsResult = await settings; + if (settingsResult) { + apiUrl = settingsResult['apiUrl']; + } + return axios + .post(`${apiUrl}/v1/manufacturers`, manufacturer) + .then((response) => response.data); +}; + +export const useAddManufacturer = (): UseMutationResult< + AddManufacturerResponse, + AxiosError, + AddManufacturer +> => { + const queryClient = useQueryClient(); + return useMutation( + (manufacturer: AddManufacturer) => addManufacturer(manufacturer), + { + onError: (error) => { + console.log('Got error ' + error.message); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['Manufacturers'] }); + }, + } + ); +}; diff --git a/src/app.types.tsx b/src/app.types.tsx index 7083244f6..df01a40ad 100644 --- a/src/app.types.tsx +++ b/src/app.types.tsx @@ -36,10 +36,34 @@ export interface CatalogueCategory { export interface Manufacturer { name: string; url: string; - address: string; + address: Address; + telephone: string; id: string; } +export interface AddManufacturer { + name: string; + url?: string; + address: Address | undefined; + telephone?: string; +} + +export interface AddManufacturerResponse { + name: string; + code: string; + url: string; + address: Address; + telephone: string; + id: string; +} + +export interface ManufacturerDetail { + name: string; + url: string; + address: Address; + telephone: string; +} + export interface CatalogueCategoryFormData { name: string; type: string; @@ -96,6 +120,13 @@ export interface ErrorParsing { detail: string; } +interface Address { + building_number: string; + street_name: string; + town?: string; + county?: string; + postcode: string; +} export interface CatalogueCategoryTransferState { name: string; message: string; diff --git a/src/manufacturer/AddmanufacturerDialog.component.test.tsx b/src/manufacturer/AddmanufacturerDialog.component.test.tsx new file mode 100644 index 000000000..394291904 --- /dev/null +++ b/src/manufacturer/AddmanufacturerDialog.component.test.tsx @@ -0,0 +1,321 @@ +import React from 'react'; +import { fireEvent, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import AddManufacturerDialog, { + AddManufacturerDialogProps, +} from './manufacturerDialog.component'; +import { renderComponentWithBrowserRouter } from '../setupTests'; +import axios from 'axios'; + +describe('Add manufacturer dialog', () => { + const onClose = jest.fn(); + const onChangeManufacturerDetails = jest.fn(); + let props: AddManufacturerDialogProps; + let user; + let axiosPostSpy; + const createView = () => { + return renderComponentWithBrowserRouter( + + ); + }; + beforeEach(() => { + props = { + open: true, + onClose: onClose, + onChangeManufacturerDetails: onChangeManufacturerDetails, + manufacturer: { + name: '', + url: '', + address: { + building_number: '', + street_name: '', + town: '', + county: '', + postcode: '', + }, + telephone: '', + }, + }; + user = userEvent.setup(); + axiosPostSpy = jest.spyOn(axios, 'post'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('renders text fields correctly', async () => { + createView(); + expect(screen.getByLabelText('Name *')).toBeInTheDocument(); + expect(screen.getByLabelText('URL')).toBeInTheDocument(); + expect(screen.getByLabelText('Building number *')).toBeInTheDocument(); + expect(screen.getByLabelText('Street name *')).toBeInTheDocument(); + expect(screen.getByLabelText('Town')).toBeInTheDocument(); + expect(screen.getByLabelText('County')).toBeInTheDocument(); + expect(screen.getByLabelText('Post/Zip code *')).toBeInTheDocument(); + expect(screen.getByLabelText('Telephone number')).toBeInTheDocument(); + }); + + it('adds manufacturer correctly', async () => { + props.manufacturer = { + name: 'Manufacturer D', + + url: 'http://test.co.uk', + address: { + building_number: '1', + street_name: 'Example Street', + town: 'Oxford', + county: 'Oxfordshire', + postcode: 'OX1 2AB', + }, + telephone: '07349612203', + }; + createView(); + + const saveButton = screen.getByRole('button', { name: 'Save' }); + await user.click(saveButton); + + expect(axiosPostSpy).toHaveBeenCalledWith('/v1/manufacturers', { + address: { + building_number: '1', + county: 'Oxfordshire', + postcode: 'OX1 2AB', + street_name: 'Example Street', + town: 'Oxford', + }, + name: 'Manufacturer D', + telephone: '07349612203', + url: 'http://test.co.uk', + }); + + expect(onClose).toHaveBeenCalled(); + }); + + it('calls onClose when Close button is clicked', async () => { + createView(); + const cancelButton = screen.getByRole('button', { name: 'Cancel' }); + await user.click(cancelButton); + + await waitFor(() => { + expect(onClose).toHaveBeenCalled(); + }); + }); + + it('duplicate manufacturer name displays warning message', async () => { + props.manufacturer = { + name: 'Manufacturer A', + + url: 'http://test.co.uk', + address: { + building_number: '1', + street_name: 'Example Street', + town: 'Oxford', + county: 'Oxfordshire', + postcode: 'OX1 2AB', + }, + telephone: '07349612203', + }; + + createView(); + + const saveButton = screen.getByRole('button', { name: 'Save' }); + await user.click(saveButton); + + await waitFor(() => { + expect( + screen.getByText('A manufacturer with the same name already exists.') + ).toBeInTheDocument(); + }); + expect(onClose).not.toHaveBeenCalled(); + }); + + it('empty fields that are required display warning', async () => { + createView(); + + const saveButton = screen.getByRole('button', { name: 'Save' }); + await user.click(saveButton); + + expect(screen.getByText('Please enter a name.')).toBeInTheDocument(); + expect( + screen.getByText('Please enter a building number.') + ).toBeInTheDocument(); + expect(screen.getByText('Please enter a street name.')).toBeInTheDocument(); + expect( + screen.getByText('Please enter a post code or zip code.') + ).toBeInTheDocument(); + expect(onClose).not.toHaveBeenCalled(); + }); + + it('invalid url displays error', async () => { + props.manufacturer = { + name: 'Manufacturer A', + + url: 'invalid', + address: { + building_number: '1', + street_name: 'Example Street', + town: 'Oxford', + county: 'Oxfordshire', + postcode: 'OX1 2AB', + }, + telephone: '07349612203', + }; + + createView(); + + const saveButton = screen.getByRole('button', { name: 'Save' }); + await user.click(saveButton); + + expect(screen.getByText('Please enter a valid URL')).toBeInTheDocument(); + expect(onClose).not.toHaveBeenCalled(); + }); + + it('handles manufacturer name input correctly', async () => { + const newManufacturerName = 'Test Manufacturer'; + + createView(); + + const manufacturerNameInput = screen.getByLabelText('Name *'); + + fireEvent.change(manufacturerNameInput, { + target: { value: newManufacturerName }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + name: newManufacturerName, + }); + }); + + it('handles manufacturer url input correctly', async () => { + const newManufacturerURL = 'Test'; + + createView(); + + const manufacturerURLInput = screen.getByLabelText('URL'); + + fireEvent.change(manufacturerURLInput, { + target: { value: newManufacturerURL }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + url: newManufacturerURL, + }); + }); + + it('handles manufacturer building number input correctly', async () => { + const newManufacturerBuildingNumber = 'Test'; + + createView(); + + const manufacturerBuildingNumberInput = + screen.getByLabelText('Building number *'); + + fireEvent.change(manufacturerBuildingNumberInput, { + target: { value: newManufacturerBuildingNumber }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + address: { + ...props.manufacturer.address, + building_number: newManufacturerBuildingNumber, + }, + }); + }); + + it('handles manufacturer street name input correctly', async () => { + const newManufacturerStreetName = 'Test'; + + createView(); + + const manufacturerStreetNameInput = screen.getByLabelText('Street name *'); + + fireEvent.change(manufacturerStreetNameInput, { + target: { value: newManufacturerStreetName }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + address: { + ...props.manufacturer.address, + street_name: newManufacturerStreetName, + }, + }); + }); + + it('handles manufacturer town input correctly', async () => { + const newManufacturerTown = 'Test'; + + createView(); + + const manufacturerTownInput = screen.getByLabelText('Town'); + + fireEvent.change(manufacturerTownInput, { + target: { value: newManufacturerTown }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + address: { ...props.manufacturer.address, town: newManufacturerTown }, + }); + }); + + it('handles manufacturer county input correctly', async () => { + const newManufacturerCounty = 'Test'; + + createView(); + + const manufacturerCountyInput = screen.getByLabelText('County'); + + fireEvent.change(manufacturerCountyInput, { + target: { value: newManufacturerCounty }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + address: { + ...props.manufacturer.address, + county: newManufacturerCounty, + }, + }); + }); + + it('handles manufacturer post code input correctly', async () => { + const newManufacturerpostcode = 'Test'; + + createView(); + + const manufacturerpostcodeInput = screen.getByLabelText('Post/Zip code *'); + + fireEvent.change(manufacturerpostcodeInput, { + target: { value: newManufacturerpostcode }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + address: { + ...props.manufacturer.address, + postcode: newManufacturerpostcode, + }, + }); + }); + + it('handles manufacturer telephone input correctly', async () => { + const newManufacturerTelephone = 'Test'; + + createView(); + + const manufacturerTelephoneInput = + screen.getByLabelText('Telephone number'); + + fireEvent.change(manufacturerTelephoneInput, { + target: { value: newManufacturerTelephone }, + }); + + expect(onChangeManufacturerDetails).toHaveBeenCalledWith({ + ...props.manufacturer, + telephone: newManufacturerTelephone, + }); + }); +}); diff --git a/src/manufacturer/manufacturer.component.test.tsx b/src/manufacturer/manufacturer.component.test.tsx index 3de503084..b0d2c0184 100644 --- a/src/manufacturer/manufacturer.component.test.tsx +++ b/src/manufacturer/manufacturer.component.test.tsx @@ -22,6 +22,7 @@ describe('Manufacturer', () => { expect(screen.getByText('Name')).toBeInTheDocument(); expect(screen.getByText('URL')).toBeInTheDocument(); expect(screen.getByText('Address')).toBeInTheDocument(); + expect(screen.getByText('Telephone')).toBeInTheDocument(); }); it('renders table data correctly', async () => { @@ -33,11 +34,20 @@ describe('Manufacturer', () => { expect(screen.getByText('Manufacturer B')).toBeInTheDocument(); expect(screen.getByText('Manufacturer C')).toBeInTheDocument(); expect(screen.getByText('http://example.com')).toBeInTheDocument(); + expect(screen.getByText('http://test.com')).toBeInTheDocument(); expect(screen.getByText('http://test.co.uk')).toBeInTheDocument(); - expect(screen.getByText('http://123test.com')).toBeInTheDocument(); - expect(screen.getByText('10 My Street')).toBeInTheDocument(); - expect(screen.getByText('11 My Street')).toBeInTheDocument(); - expect(screen.getByText('12 My Street')).toBeInTheDocument(); + expect( + screen.getByText('1 Example Street Oxford Oxfordshire OX1 2AB') + ).toBeInTheDocument(); + expect( + screen.getByText('2 Example Street Oxford Oxfordshire OX1 2AB') + ).toBeInTheDocument(); + expect( + screen.getByText('3 Example Street Oxford Oxfordshire OX1 2AB') + ).toBeInTheDocument(); + expect(screen.getByText('07334893348')).toBeInTheDocument(); + expect(screen.getByText('07294958549')).toBeInTheDocument(); + expect(screen.getByText('07934303412')).toBeInTheDocument(); }); it('manufacturer url has a href so therefore links to new webpage', async () => { diff --git a/src/manufacturer/manufacturer.component.tsx b/src/manufacturer/manufacturer.component.tsx index e1d34d02f..0dd30b2f3 100644 --- a/src/manufacturer/manufacturer.component.tsx +++ b/src/manufacturer/manufacturer.component.tsx @@ -10,13 +10,33 @@ import { useTheme, Box, Link, + Button, } from '@mui/material'; import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; import React from 'react'; import { useManufacturers } from '../api/manufacturer'; +import AddManufacturerDialog from './manufacturerDialog.component'; +import { ManufacturerDetail } from '../app.types'; function Manufacturer() { + const [addManufacturer, setAddManufacturer] = + React.useState({ + name: '', + url: '', + address: { + building_number: '', + street_name: '', + town: '', + county: '', + postcode: '', + }, + telephone: '', + }); + + const [addManufacturerDialogOpen, setAddManufacturerDialogOpen] = + React.useState(false); + const { data: ManufacturerData } = useManufacturers(); const [hoveredRow, setHoveredRow] = React.useState(null); @@ -24,119 +44,168 @@ function Manufacturer() { const theme = useTheme(); return ( - - - - - + + + setAddManufacturerDialogOpen(false)} + manufacturer={addManufacturer} + onChangeManufacturerDetails={setAddManufacturer} + /> + + +
+ + - Actions - + + Actions + - - Name - - - URL - - - Address - - - - - {ManufacturerData && - ManufacturerData.map((item, index) => ( - setHoveredRow(index)} - onMouseLeave={() => setHoveredRow(null)} - style={{ - backgroundColor: - hoveredRow === index - ? theme.palette.action.hover - : 'inherit', + - - - - - - - - - - - - {item.name} - - - - {item.url} - - - Name + + + URL + + + Address + + + Telephone + + + + + {ManufacturerData && + ManufacturerData.map((item, index) => ( + setHoveredRow(index)} + onMouseLeave={() => setHoveredRow(null)} + style={{ + backgroundColor: + hoveredRow === index + ? theme.palette.action.hover + : 'inherit', }} + key={item.id} + aria-label={`${item.name} row`} > - {item.address} - - - ))} - -
-
+ + + + + + + + + + + + {item.name} + + + + {item.url} + + + + {item.address.building_number + + ' \n' + + item.address.street_name + + ' \n' + + item.address.town + + ' \n' + + item.address.county + + ' \n' + + item.address.postcode} + + + {item.telephone} + + + ))} + + + + ); } diff --git a/src/manufacturer/manufacturerDialog.component.tsx b/src/manufacturer/manufacturerDialog.component.tsx new file mode 100644 index 000000000..08687134f --- /dev/null +++ b/src/manufacturer/manufacturerDialog.component.tsx @@ -0,0 +1,368 @@ +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Grid, + TextField, + Typography, +} from '@mui/material'; + +import React from 'react'; + +import { AddManufacturer, ManufacturerDetail } from '../app.types'; +import { useAddManufacturer } from '../api/manufacturer'; +import { AxiosError } from 'axios'; + +export interface AddManufacturerDialogProps { + open: boolean; + onClose: () => void; + onChangeManufacturerDetails: (manufacturer: ManufacturerDetail) => void; + manufacturer: ManufacturerDetail; +} +function isValidUrl(url: string) { + try { + const parsedUrl = new URL(url); + return ( + (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') && + parsedUrl.hostname.includes('.') // Checks for the typical top-level domain + ); + } catch (error) { + return false; + } +} + +function AddManufacturerDialog(props: AddManufacturerDialogProps) { + const { open, onClose, manufacturer, onChangeManufacturerDetails } = props; + + const [nameError, setNameError] = React.useState(false); + const [nameErrorMessage, setNameErrorMessage] = React.useState< + string | undefined + >(undefined); + const [URlerror, setURLError] = React.useState(false); + const [URLErrorMessage, setURLErrorMessage] = React.useState< + string | undefined + >(undefined); + const [addressBuildingNumberError, setAddressBuildingNumberError] = + React.useState(false); + const [ + addressBuildingNumberErrorMessage, + setAddressBuildingNumberErrorMessage, + ] = React.useState(undefined); + const [addressStreetNameError, setAddressStreetNameError] = + React.useState(false); + const [addressStreetNameErrorMessage, setaddressStreetNameErrorMessage] = + React.useState(undefined); + const [addresspostcodeError, setAddresspostcodeError] = React.useState(false); + const [AddresspostcodeErrorMessage, setAddresspostcodeErrorMessage] = + React.useState(undefined); + + const { mutateAsync: addManufacturer } = useAddManufacturer(); + + const handleClose = React.useCallback(() => { + onChangeManufacturerDetails({ + name: '', + url: '', + address: { + building_number: '', + street_name: '', + town: '', + county: '', + postcode: '', + }, + telephone: '', + }); + setNameError(false); + setNameErrorMessage(undefined); + setURLError(false); + setURLErrorMessage(undefined); + setAddressBuildingNumberError(false); + setAddressBuildingNumberErrorMessage(undefined); + setAddressStreetNameError(false); + setaddressStreetNameErrorMessage(undefined); + setAddresspostcodeError(false); + setAddresspostcodeErrorMessage(undefined); + onClose(); + }, [onClose, onChangeManufacturerDetails]); + + const handleManufacturer = React.useCallback(() => { + let hasErrors = false; + + //check url is valid + if (manufacturer.url) { + if (!isValidUrl(manufacturer.url)) { + hasErrors = true; + setURLError(true); + setURLErrorMessage('Please enter a valid URL'); + } + } + + //check name + if (!manufacturer.name || manufacturer.name?.trim().length === 0) { + hasErrors = true; + setNameError(true); + setNameErrorMessage('Please enter a name.'); + } + //check building number + if ( + !manufacturer.address?.building_number || + manufacturer.address.building_number.trim().length === 0 + ) { + hasErrors = true; + setAddressBuildingNumberError(true); + setAddressBuildingNumberErrorMessage('Please enter a building number.'); + } + //check street name + if ( + !manufacturer.address?.street_name || + manufacturer.address.street_name?.trim().length === 0 + ) { + hasErrors = true; + setAddressStreetNameError(true); + setaddressStreetNameErrorMessage('Please enter a street name.'); + } + //check post code + if ( + !manufacturer.address?.postcode || + manufacturer.address.postcode?.trim().length === 0 + ) { + hasErrors = true; + setAddresspostcodeError(true); + setAddresspostcodeErrorMessage('Please enter a post code or zip code.'); + } + + if (hasErrors) { + return; + } + + const manufacturerToAdd: AddManufacturer = { + name: manufacturer.name, + url: manufacturer.url, + address: { + building_number: manufacturer.address.building_number, + street_name: manufacturer.address.street_name, + town: manufacturer.address.town, + county: manufacturer.address.county, + postcode: manufacturer.address.postcode, + }, + telephone: manufacturer.telephone, + }; + + addManufacturer(manufacturerToAdd) + .then((response) => handleClose()) + .catch((error: AxiosError) => { + console.log(error.response?.status, manufacturer.name); + + if (error.response?.status === 409) { + setNameError(true); + setNameErrorMessage( + 'A manufacturer with the same name already exists.' + ); + } + }); + }, [manufacturer, addManufacturer, handleClose]); + + return ( + + Add Manufacturer + + + + { + onChangeManufacturerDetails({ + ...manufacturer, + name: event.target.value, + }); + setNameError(false); + setNameErrorMessage(undefined); + }} + error={nameError} + helperText={nameError && nameErrorMessage} + fullWidth + > + + + { + onChangeManufacturerDetails({ + ...manufacturer, + url: event.target.value, + }); + setURLError(false); + setURLErrorMessage(undefined); + }} + error={URlerror} + helperText={URlerror && URLErrorMessage} + fullWidth + /> + + + Address + + + { + onChangeManufacturerDetails({ + ...manufacturer, + address: { + ...manufacturer.address, + building_number: event.target.value, + }, + }); + setAddressBuildingNumberError(false); + setAddressBuildingNumberErrorMessage(undefined); + }} + error={addressBuildingNumberError} + helperText={ + addressBuildingNumberError && addressBuildingNumberErrorMessage + } + fullWidth + /> + + + { + onChangeManufacturerDetails({ + ...manufacturer, + address: { + ...manufacturer.address, + street_name: event.target.value, + }, + }); + setAddressStreetNameError(false); + setaddressStreetNameErrorMessage(undefined); + }} + error={addressStreetNameError} + helperText={ + addressStreetNameError && addressStreetNameErrorMessage + } + fullWidth + /> + + + { + onChangeManufacturerDetails({ + ...manufacturer, + address: { + ...manufacturer.address, + town: event.target.value, + }, + }); + }} + fullWidth + /> + + + { + onChangeManufacturerDetails({ + ...manufacturer, + address: { + ...manufacturer.address, + county: event.target.value, + }, + }); + }} + fullWidth + /> + + + { + onChangeManufacturerDetails({ + ...manufacturer, + address: { + ...manufacturer.address, + postcode: event.target.value, + }, + }); + setAddresspostcodeError(false); + setAddresspostcodeErrorMessage(undefined); + }} + error={addresspostcodeError} + helperText={addresspostcodeError && AddresspostcodeErrorMessage} + fullWidth + /> + + + { + onChangeManufacturerDetails({ + ...manufacturer, + telephone: event.target.value, + }); + }} + fullWidth + /> + + + + + + + + + + + + ); +} + +export default AddManufacturerDialog; diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 2180f9bda..97ccc67a0 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -1,6 +1,7 @@ import { rest } from 'msw'; import CatalogueCategoryJSON from './CatalogueCategory.json'; import ManufacturerJSON from './manufacturer.json'; +import { AddManufacturer } from '../app.types'; import CatalogueItemJSON from './CatalogueItems.json'; import CatalogueBreadcrumbsJSON from './CatalogueBreadcrumbs.json'; import SystemsJSON from './Systems.json'; @@ -117,10 +118,39 @@ export const handlers = [ return res(ctx.status(200), ctx.json(data)); }), - rest.get('/v1/manufacturer', (req, res, ctx) => { + rest.get('/v1/manufacturers', (req, res, ctx) => { return res(ctx.status(200), ctx.json(ManufacturerJSON)); }), + rest.post('/v1/manufacturers', async (req, res, ctx) => { + const body = (await req.json()) as AddManufacturer; + + if (!body.name) { + return res(ctx.status(422), ctx.json('')); + } + if (body.name === 'Manufacturer A') { + return res(ctx.status(409), ctx.json('')); + } + + return res( + ctx.status(200), + ctx.json({ + name: 'Manufacturer D', + code: 'manufacturer-d', + url: 'http://test.co.uk', + address: { + building_number: '1', + street_name: 'Example Street', + town: 'Oxford', + county: 'Oxfordshire', + postcode: 'OX1 2AB', + }, + telephone: '07349612203', + id: '4', + }) + ); + }), + rest.delete('/v1/catalogue-categories/:id', (req, res, ctx) => { const { id } = req.params; const validCatalogueCategory = CatalogueCategoryJSON.find( diff --git a/src/mocks/manufacturer.json b/src/mocks/manufacturer.json index 0d773b394..8d741b298 100644 --- a/src/mocks/manufacturer.json +++ b/src/mocks/manufacturer.json @@ -4,20 +4,41 @@ "name": "Manufacturer A", "code": "manufacturer-a", "url": "http://example.com", - "address": "10 My Street" + "address": { + "building_number": "1", + "street_name": "Example Street", + "town": "Oxford", + "county": "Oxfordshire", + "postcode": "OX1 2AB" + }, + "telephone": "07334893348" }, { "id": "2", "name": "Manufacturer B", "code": "manufacturer-b", - "url": "http://test.co.uk", - "address": "11 My Street" + "url": "http://test.com", + "address": { + "building_number": "2", + "street_name": "Example Street", + "town": "Oxford", + "county": "Oxfordshire", + "postcode": "OX1 2AB" + }, + "telephone": "07294958549" }, { "id": "3", "name": "Manufacturer C", "code": "manufacturer-c", - "url": "http://123test.com", - "address": "12 My Street" + "url": "http://test.co.uk", + "address": { + "building_number": "3", + "street_name": "Example Street", + "town": "Oxford", + "county": "Oxfordshire", + "postcode": "OX1 2AB" + }, + "telephone": "07934303412" } ]