mirror of
				https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
				synced 2025-10-31 02:07:45 +00:00 
			
		
		
		
	Added eslint and prettier to project
This commit is contained in:
		
							
								
								
									
										1
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.eslintignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | /src/assets | ||||||
| @@ -11,10 +11,12 @@ | |||||||
|     "jest": true |     "jest": true | ||||||
| }, | }, | ||||||
| "rules": { | "rules": { | ||||||
|     "max-len": ["error", {"code": 100}], |     "max-len": ["error", {"code": 150}], | ||||||
|     "prefer-promise-reject-errors": ["off"], |     "prefer-promise-reject-errors": ["off"], | ||||||
|     "react/jsx-filename-extension": ["off"], |     "react/jsx-filename-extension": ["off"], | ||||||
|     "react/prop-types": ["warn"], |     "react/prop-types": ["warn"], | ||||||
|     "no-return-assign": ["off"] |     "no-return-assign": ["off"], | ||||||
|  |     "react/jsx-props-no-spreading": ["off"], | ||||||
|  |     "react/destructuring-assignment": ["off"] | ||||||
|   } |   } | ||||||
| } | } | ||||||
							
								
								
									
										21566
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21566
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -29,6 +29,7 @@ | |||||||
|     "react-select": "^4.3.0", |     "react-select": "^4.3.0", | ||||||
|     "react-widgets": "^5.1.1", |     "react-widgets": "^5.1.1", | ||||||
|     "reactstrap": "^8.9.0", |     "reactstrap": "^8.9.0", | ||||||
|  |     "redux": "^4.1.0", | ||||||
|     "sass": "^1.32.8", |     "sass": "^1.32.8", | ||||||
|     "uuid": "^8.3.2" |     "uuid": "^8.3.2" | ||||||
|   }, |   }, | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/App.js
									
									
									
									
									
								
							| @@ -5,15 +5,15 @@ import { useSelector, useDispatch } from 'react-redux'; | |||||||
|  |  | ||||||
| const loading = ( | const loading = ( | ||||||
|   <div className="pt-3 text-center"> |   <div className="pt-3 text-center"> | ||||||
|     <div className="sk-spinner sk-spinner-pulse"></div> |     <div className="sk-spinner sk-spinner-pulse" /> | ||||||
|   </div> |   </div> | ||||||
| ) | ); | ||||||
|  |  | ||||||
| const TheLayout = React.lazy(() => import('./containers/TheLayout')); | const TheLayout = React.lazy(() => import('./containers/TheLayout')); | ||||||
| const Login = React.lazy(() => import('./views/pages/Login')); | const Login = React.lazy(() => import('./views/pages/Login')); | ||||||
|  |  | ||||||
| const App = () => { | const App = () => { | ||||||
|     const isLoggedIn  = useSelector(state => state.connected); |   const isLoggedIn = useSelector((state) => state.connected); | ||||||
|   const dispatch = useDispatch(); |   const dispatch = useDispatch(); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
| @@ -27,7 +27,11 @@ const App = () => { | |||||||
|     <HashRouter> |     <HashRouter> | ||||||
|       <React.Suspense fallback={loading}> |       <React.Suspense fallback={loading}> | ||||||
|         <Switch> |         <Switch> | ||||||
|                     <Route path="/" name="Devices" render={props => isLoggedIn ?  <TheLayout {...props}/> : <Login {...props}/>} /> |           <Route | ||||||
|  |             path="/" | ||||||
|  |             name="Devices" | ||||||
|  |             render={(props) => (isLoggedIn ? <TheLayout {...props} /> : <Login {...props} />)} | ||||||
|  |           /> | ||||||
|         </Switch> |         </Switch> | ||||||
|       </React.Suspense> |       </React.Suspense> | ||||||
|     </HashRouter> |     </HashRouter> | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ test('renders learn react link', () => { | |||||||
|   const { getByText } = render( |   const { getByText } = render( | ||||||
|     <Provider store={store}> |     <Provider store={store}> | ||||||
|       <App /> |       <App /> | ||||||
|     </Provider> |     </Provider>, | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   expect(getByText(/learn/i)).toBeInTheDocument(); |   expect(getByText(/learn/i)).toBeInTheDocument(); | ||||||
|   | |||||||
| @@ -1,7 +1,3 @@ | |||||||
| import { sygnet } from './sygnet' |  | ||||||
| import { logo } from './logo' |  | ||||||
| import { logoNegative } from './logo-negative' |  | ||||||
|  |  | ||||||
| import { | import { | ||||||
|   cibSkype, |   cibSkype, | ||||||
|   cibFacebook, |   cibFacebook, | ||||||
| @@ -27,16 +23,12 @@ import { | |||||||
|   cibPaypal, |   cibPaypal, | ||||||
|   cibGooglePay, |   cibGooglePay, | ||||||
|   cibCcAmex, |   cibCcAmex, | ||||||
| } from '@coreui/icons' |  | ||||||
| import { |  | ||||||
|   cifUs, |   cifUs, | ||||||
|   cifBr, |   cifBr, | ||||||
|   cifIn, |   cifIn, | ||||||
|   cifFr, |   cifFr, | ||||||
|   cifEs, |   cifEs, | ||||||
|   cifPl |   cifPl, | ||||||
| } from '@coreui/icons' |  | ||||||
| import { |  | ||||||
|   cilAlignCenter, |   cilAlignCenter, | ||||||
|   cilAlignLeft, |   cilAlignLeft, | ||||||
|   cilAlignRight, |   cilAlignRight, | ||||||
| @@ -128,14 +120,16 @@ import { | |||||||
|   cilUserUnfollow, |   cilUserUnfollow, | ||||||
|   cilX, |   cilX, | ||||||
|   cilXCircle, |   cilXCircle, | ||||||
|   cilWarning |   cilWarning, | ||||||
| } from '@coreui/icons' | } from '@coreui/icons'; | ||||||
|  | import { sygnet } from './sygnet'; | ||||||
|  | import { logo } from './logo'; | ||||||
|  | import { logoNegative } from './logo-negative'; | ||||||
|  |  | ||||||
| export const icons = Object.assign({}, { | export const icons = { | ||||||
|   sygnet, |   sygnet, | ||||||
|   logo, |   logo, | ||||||
|   logoNegative |   logoNegative, | ||||||
| }, { |  | ||||||
|   cilAlignCenter, |   cilAlignCenter, | ||||||
|   cilAlignLeft, |   cilAlignLeft, | ||||||
|   cilAlignRight, |   cilAlignRight, | ||||||
| @@ -227,15 +221,13 @@ export const icons = Object.assign({}, { | |||||||
|   cilUserUnfollow, |   cilUserUnfollow, | ||||||
|   cilX, |   cilX, | ||||||
|   cilXCircle, |   cilXCircle, | ||||||
|   cilWarning |   cilWarning, | ||||||
| }, { |  | ||||||
|   cifUs, |   cifUs, | ||||||
|   cifBr, |   cifBr, | ||||||
|   cifIn, |   cifIn, | ||||||
|   cifFr, |   cifFr, | ||||||
|   cifEs, |   cifEs, | ||||||
|   cifPl |   cifPl, | ||||||
| }, { |  | ||||||
|   cibSkype, |   cibSkype, | ||||||
|   cibFacebook, |   cibFacebook, | ||||||
|   cibTwitter, |   cibTwitter, | ||||||
| @@ -259,5 +251,5 @@ export const icons = Object.assign({}, { | |||||||
|   cibStripe, |   cibStripe, | ||||||
|   cibPaypal, |   cibPaypal, | ||||||
|   cibGooglePay, |   cibGooglePay, | ||||||
|   cibCcAmex |   cibCcAmex, | ||||||
| }) | }; | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| export const logoNegative = ['608 134', ` | export const logoNegative = [ | ||||||
|  |   '608 134', | ||||||
|  |   ` | ||||||
|   <title>coreui react pro logo</title> |   <title>coreui react pro logo</title> | ||||||
|   <g> |   <g> | ||||||
|     <g style="fill:#80d0ff;"> |     <g style="fill:#80d0ff;"> | ||||||
| @@ -27,4 +29,5 @@ export const logoNegative = ['608 134', ` | |||||||
|       </g> |       </g> | ||||||
|     </g> |     </g> | ||||||
|   </g> |   </g> | ||||||
| `] | `, | ||||||
|  | ]; | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| export const logo = ['608 134', ` | export const logo = [ | ||||||
|  |   '608 134', | ||||||
|  |   ` | ||||||
|   <title>coreui react pro</title> |   <title>coreui react pro</title> | ||||||
|   <g> |   <g> | ||||||
|     <g style="fill:#00a1ff"> |     <g style="fill:#00a1ff"> | ||||||
| @@ -26,4 +28,5 @@ export const logo = ['608 134', ` | |||||||
|       </g> |       </g> | ||||||
|     </g> |     </g> | ||||||
|   </g> |   </g> | ||||||
| `] | `, | ||||||
|  | ]; | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| export const sygnet = ['160 160', ` | export const sygnet = [ | ||||||
|  |   '160 160', | ||||||
|  |   ` | ||||||
|   <title>coreui logo</title> |   <title>coreui logo</title> | ||||||
|   <g> |   <g> | ||||||
|     <g style="fill:#fff;"> |     <g style="fill:#fff;"> | ||||||
| @@ -6,4 +8,5 @@ export const sygnet = ['160 160', ` | |||||||
|       <path d="M103.0216,93.0379h-2.866a4,4,0,0,0-1.9246.4935L80.95,103.0167,61,91.4981V68.5206L80.95,57.002l17.2894,9.455a4,4,0,0,0,1.9192.4905h2.8632a2,2,0,0,0,2-2V62.2357a2,2,0,0,0-1.04-1.7547L84.793,49.9854a8.0391,8.0391,0,0,0-7.8428.09L57,61.5929A8.0243,8.0243,0,0,0,53,68.5216v22.976a8,8,0,0,0,4,6.9283l19.95,11.5185a8.0422,8.0422,0,0,0,7.8433.0879l19.19-10.5311a2,2,0,0,0,1.0378-1.7534v-2.71A2,2,0,0,0,103.0216,93.0379Z"/> |       <path d="M103.0216,93.0379h-2.866a4,4,0,0,0-1.9246.4935L80.95,103.0167,61,91.4981V68.5206L80.95,57.002l17.2894,9.455a4,4,0,0,0,1.9192.4905h2.8632a2,2,0,0,0,2-2V62.2357a2,2,0,0,0-1.04-1.7547L84.793,49.9854a8.0391,8.0391,0,0,0-7.8428.09L57,61.5929A8.0243,8.0243,0,0,0,53,68.5216v22.976a8,8,0,0,0,4,6.9283l19.95,11.5185a8.0422,8.0422,0,0,0,7.8433.0879l19.19-10.5311a2,2,0,0,0,1.0378-1.7534v-2.71A2,2,0,0,0,103.0216,93.0379Z"/> | ||||||
|     </g> |     </g> | ||||||
|   </g> |   </g> | ||||||
| `] | `, | ||||||
|  | ]; | ||||||
|   | |||||||
| @@ -1,12 +1,5 @@ | |||||||
| import React, { useState } from 'react' | import React, { useState } from 'react'; | ||||||
| import { | import { CButton, CCard, CCardHeader, CCardBody, CRow, CCol } from '@coreui/react'; | ||||||
| 	CButton, |  | ||||||
| 	CCard, |  | ||||||
| 	CCardHeader, |  | ||||||
| 	CCardBody, |  | ||||||
| 	CRow, |  | ||||||
| 	CCol |  | ||||||
| } from '@coreui/react' |  | ||||||
| import ActionModalWidget from '../widgets/ActionModalWidget'; | import ActionModalWidget from '../widgets/ActionModalWidget'; | ||||||
| import FirmwareUpgradeModal from './FirmwareUpgradeModal'; | import FirmwareUpgradeModal from './FirmwareUpgradeModal'; | ||||||
|  |  | ||||||
| @@ -14,114 +7,64 @@ const DeviceActions = () => { | |||||||
|   const [showRebootModal, setShowRebootModal] = useState(false); |   const [showRebootModal, setShowRebootModal] = useState(false); | ||||||
|   const [showBlinkModal, setShowBlinkModal] = useState(false); |   const [showBlinkModal, setShowBlinkModal] = useState(false); | ||||||
|   const [showUpgradeModal, setShowUpgradeModal] = useState(false); |   const [showUpgradeModal, setShowUpgradeModal] = useState(false); | ||||||
| 	const [firmwareUri, setFirmwareUri] = useState(''); |  | ||||||
| 	const [validField, setValidField] = useState(true); |  | ||||||
|  |  | ||||||
| 	const toggleRebootModal = (e) => { |   const toggleRebootModal = () => { | ||||||
|     setShowRebootModal(!showRebootModal); |     setShowRebootModal(!showRebootModal); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
| 	const toggleBlinkModal = (e) => { |   const toggleBlinkModal = () => { | ||||||
|     setShowBlinkModal(!showBlinkModal); |     setShowBlinkModal(!showBlinkModal); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
| 	const toggleUpgradeModal = (e) => { |   const toggleUpgradeModal = () => { | ||||||
|     setShowUpgradeModal(!showUpgradeModal); |     setShowUpgradeModal(!showUpgradeModal); | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const formChange = (fieldValue) => { |  | ||||||
| 		if(!validField){ |  | ||||||
| 			setValidField(true); |  | ||||||
| 		} |  | ||||||
| 		setFirmwareUri(fieldValue); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const formValidation = () => { |  | ||||||
| 		if (firmwareUri.trim() === ''){ |  | ||||||
| 			setValidField(false); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 		return true; |  | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CCard> |     <CCard> | ||||||
| 			<CCardHeader> |       <CCardHeader>Device Actions</CCardHeader> | ||||||
| 					Device Actions |  | ||||||
| 			</CCardHeader> |  | ||||||
|       <CCardBody> |       <CCardBody> | ||||||
|         <CRow> |         <CRow> | ||||||
|           <CCol> |           <CCol> | ||||||
| 						<CButton block onClick = { toggleRebootModal } color="primary">Reboot</CButton> |             <CButton block onClick={toggleRebootModal} color="primary"> | ||||||
|  |               Reboot | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|           <CCol> |           <CCol> | ||||||
| 						<CButton block onClick = { toggleBlinkModal } color="primary">Blink</CButton> |             <CButton block onClick={toggleBlinkModal} color="primary"> | ||||||
|  |               Blink | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|         <CRow style={{ marginTop: '10px' }}> |         <CRow style={{ marginTop: '10px' }}> | ||||||
|           <CCol> |           <CCol> | ||||||
| 						<CButton block color="primary" onClick = { toggleUpgradeModal }>Firmware Upgrade</CButton> |             <CButton block color="primary" onClick={toggleUpgradeModal}> | ||||||
| 					</CCol> |               Firmware Upgrade | ||||||
| 					<CCol> |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|  |           <CCol /> | ||||||
|         </CRow> |         </CRow> | ||||||
|       </CCardBody> |       </CCardBody> | ||||||
|       <ActionModalWidget |       <ActionModalWidget | ||||||
|         show={showRebootModal} |         show={showRebootModal} | ||||||
|         toggleModal={toggleRebootModal} |         toggleModal={toggleRebootModal} | ||||||
| 				title='Reboot Device' |         title="Reboot Device" | ||||||
| 				directions='When would you like to reboot this device?' |         directions="When would you like to reboot this device?" | ||||||
| 				actionLabel='reboot' |         actionLabel="reboot" | ||||||
| 				action='reboot' |         action="reboot" | ||||||
|       /> |       /> | ||||||
|       <ActionModalWidget |       <ActionModalWidget | ||||||
|         show={showBlinkModal} |         show={showBlinkModal} | ||||||
|         toggleModal={toggleBlinkModal} |         toggleModal={toggleBlinkModal} | ||||||
| 				title='Blink LEDs of Device' |         title="Blink LEDs of Device" | ||||||
| 				directions='When would you like make the LEDs of this device blink?' |         directions="When would you like make the LEDs of this device blink?" | ||||||
| 				actionLabel='blink' |         actionLabel="blink" | ||||||
| 				action='leds' |         action="leds" | ||||||
|         extraParameters={{ duration: 10, pattern: 'on' }} |         extraParameters={{ duration: 10, pattern: 'on' }} | ||||||
|       /> |       /> | ||||||
| 			<FirmwareUpgradeModal |       <FirmwareUpgradeModal show={showUpgradeModal} toggleModal={setShowUpgradeModal} /> | ||||||
| 				show={showUpgradeModal} |  | ||||||
| 				toggleModal={setShowUpgradeModal} |  | ||||||
| 			/> |  | ||||||
| 			 |  | ||||||
| 			{/*<CModal  |  | ||||||
| 				show={showModal}  |  | ||||||
| 				onClose={toggleModal} |  | ||||||
| 			> |  | ||||||
| 				<CModalHeader closeButton> |  | ||||||
| 					<CModalTitle>Firmware Upgrade</CModalTitle> |  | ||||||
| 				</CModalHeader> |  | ||||||
| 				<CModalBody> |  | ||||||
| 					<p>Insert the link of the firmware version you would like to upgrade the device to.</p> |  | ||||||
| 					<CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> |  | ||||||
| 						<CInput  |  | ||||||
| 							className={'form-control', {'is-invalid' : !validField}}  |  | ||||||
| 							type="text"  |  | ||||||
| 							id="uri"  |  | ||||||
| 							name="uri-input"  |  | ||||||
| 							placeholder="https://somelocation.com/file=newversion.bin"  |  | ||||||
| 							autoComplete="firmware-uri"  |  | ||||||
| 							onChange={event => formChange(event.target.value)} |  | ||||||
| 						/> |  | ||||||
| 						<CInvalidFeedback>You need a url...</CInvalidFeedback> |  | ||||||
| 					</CForm> |  | ||||||
| 				</CModalBody> |  | ||||||
| 				<CModalFooter> |  | ||||||
| 					<CButton color="primary" onClick={event => formValidation()}>Upgrade</CButton> |  | ||||||
| 					<CButton  |  | ||||||
| 						color="secondary"  |  | ||||||
| 						onClick={toggleModal} |  | ||||||
| 					> |  | ||||||
| 						Cancel |  | ||||||
| 					</CButton> |  | ||||||
| 				</CModalFooter> |  | ||||||
| 			</CModal>*/} |  | ||||||
|     </CCard> |     </CCard> | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default DeviceActions | export default DeviceActions; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useState, useEffect } from 'react' | import React, { useState, useEffect } from 'react'; | ||||||
| import { | import { | ||||||
|   CCard, |   CCard, | ||||||
|   CCardHeader, |   CCardHeader, | ||||||
| @@ -10,57 +10,51 @@ import { | |||||||
|   CInput, |   CInput, | ||||||
|   CCollapse, |   CCollapse, | ||||||
|   CCardFooter, |   CCardFooter, | ||||||
|     CButton |   CButton, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react' | import CIcon from '@coreui/icons-react'; | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
| import { cleanTimestamp } from '../utils/helper'; | import { cleanTimestamp } from '../utils/helper'; | ||||||
| import axiosInstance from '../utils/axiosInstance'; | import axiosInstance from '../utils/axiosInstance'; | ||||||
| import { getToken } from '../utils/authHelper'; | import { getToken } from '../utils/authHelper'; | ||||||
|  |  | ||||||
|  |  | ||||||
| const DeviceConfiguration = () => { | const DeviceConfiguration = () => { | ||||||
|   const [collapse, setCollapse] = useState(false); |   const [collapse, setCollapse] = useState(false); | ||||||
|   const [device, setDevice] = useState(null); |   const [device, setDevice] = useState(null); | ||||||
|     const [loading, setLoading] = useState(false); |   const selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|     const selectedDeviceId = useSelector(state => state.selectedDeviceId); |  | ||||||
|  |  | ||||||
|   const getDevice = () => { |   const getDevice = () => { | ||||||
|     const options = { |     const options = { | ||||||
|       headers: { |       headers: { | ||||||
|                 'Accept': 'application/json', |         Accept: 'application/json', | ||||||
|                 'Authorization': `Bearer ${getToken()}` |         Authorization: `Bearer ${getToken()}`, | ||||||
|             } |       }, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|         axiosInstance.get(`/device/${selectedDeviceId}`, options) |     axiosInstance | ||||||
|  |       .get(`/device/${selectedDeviceId}`, options) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setDevice(response.data); |         setDevice(response.data); | ||||||
|             setLoading(false); |  | ||||||
|       }) |       }) | ||||||
|         .catch(error => { |       .catch((error) => { | ||||||
|             setLoading(false); |  | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
|       }); |       }); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|         setLoading(true); |  | ||||||
|     getDevice(); |     getDevice(); | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|   const toggle = (e) => { |   const toggle = (e) => { | ||||||
|     setCollapse(!collapse); |     setCollapse(!collapse); | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   if (device) { |   if (device) { | ||||||
|     return ( |     return ( | ||||||
|       <CCard> |       <CCard> | ||||||
|                 <CCardHeader> |         <CCardHeader>#{device.serialNumber} Details</CCardHeader> | ||||||
|                     #{device.serialNumber} Details |  | ||||||
|                 </CCardHeader> |  | ||||||
|         <CCardBody> |         <CCardBody> | ||||||
|           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> |           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> | ||||||
|             <CFormGroup row> |             <CFormGroup row> | ||||||
| @@ -155,7 +149,7 @@ const DeviceConfiguration = () => { | |||||||
|             </CCollapse> |             </CCollapse> | ||||||
|             <CCardFooter> |             <CCardFooter> | ||||||
|               <CButton show={collapse} color="transparent" onClick={toggle} block> |               <CButton show={collapse} color="transparent" onClick={toggle} block> | ||||||
|                                 <CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} size="lg"/> |                 <CIcon name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} size="lg" /> | ||||||
|               </CButton> |               </CButton> | ||||||
|             </CCardFooter> |             </CCardFooter> | ||||||
|           </CForm> |           </CForm> | ||||||
| @@ -166,13 +160,10 @@ const DeviceConfiguration = () => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CCard> |     <CCard> | ||||||
|                 <CCardHeader> |       <CCardHeader>Device Configuration</CCardHeader> | ||||||
|                     Device Configuration |       <CCardBody /> | ||||||
|                 </CCardHeader> |  | ||||||
|                 <CCardBody> |  | ||||||
|                 </CCardBody> |  | ||||||
|     </CCard> |     </CCard> | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default DeviceConfiguration | export default DeviceConfiguration; | ||||||
|   | |||||||
| @@ -1,13 +1,7 @@ | |||||||
| import React, { useState, useEffect } from 'react' | /* eslint-disable-rule prefer-destructuring */ | ||||||
| import { | import React, { useState, useEffect } from 'react'; | ||||||
|     CWidgetProgress, | import { CWidgetProgress, CCollapse, CButton, CDataTable, CCard, CCardBody } from '@coreui/react'; | ||||||
|     CCollapse, | import CIcon from '@coreui/icons-react'; | ||||||
|     CButton, |  | ||||||
|     CDataTable, |  | ||||||
|     CCard, |  | ||||||
|     CCardBody |  | ||||||
| } from '@coreui/react' |  | ||||||
| import CIcon from '@coreui/icons-react' |  | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
| import { cleanTimestamp } from '../utils/helper'; | import { cleanTimestamp } from '../utils/helper'; | ||||||
| import axiosInstance from '../utils/axiosInstance'; | import axiosInstance from '../utils/axiosInstance'; | ||||||
| @@ -15,51 +9,48 @@ import { getToken } from '../utils/authHelper'; | |||||||
|  |  | ||||||
| const DeviceHealth = () => { | const DeviceHealth = () => { | ||||||
|   const [collapse, setCollapse] = useState(false); |   const [collapse, setCollapse] = useState(false); | ||||||
|     const [loading, setLoading] = useState(false); |  | ||||||
|   const [details, setDetails] = useState([]); |   const [details, setDetails] = useState([]); | ||||||
|   const [healthChecks, setHealthChecks] = useState([]); |   const [healthChecks, setHealthChecks] = useState([]); | ||||||
|     const selectedDeviceId = useSelector(state => state.selectedDeviceId); |   const selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|   let sanityLevel; |   let sanityLevel; | ||||||
|   let barColor; |   let barColor; | ||||||
|  |  | ||||||
|   const toggle = (e) => { |   const toggle = (e) => { | ||||||
|     setCollapse(!collapse); |     setCollapse(!collapse); | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const getDeviceHealth = () => { |   const getDeviceHealth = () => { | ||||||
|     const options = { |     const options = { | ||||||
|       headers: { |       headers: { | ||||||
|                 'Accept': 'application/json', |         Accept: 'application/json', | ||||||
|                 'Authorization': `Bearer ${getToken()}` |         Authorization: `Bearer ${getToken()}`, | ||||||
|             } |       }, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|         axiosInstance.get(`/device/${selectedDeviceId}/healthchecks`, options) |     axiosInstance | ||||||
|  |       .get(`/device/${selectedDeviceId}/healthchecks`, options) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setHealthChecks(response.data.values); |         setHealthChecks(response.data.values); | ||||||
|             setLoading(false); |  | ||||||
|       }) |       }) | ||||||
|         .catch(error => { |       .catch((error) => { | ||||||
|             setLoading(false); |  | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
|       }); |       }); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   // Function called from the button on the table so that a user can see more details |   // Function called from the button on the table so that a user can see more details | ||||||
|   const toggleDetails = (index) => { |   const toggleDetails = (index) => { | ||||||
|         const position = details.indexOf(index) |     const position = details.indexOf(index); | ||||||
|         let newDetails = details.slice() |     let newDetails = details.slice(); | ||||||
|  |  | ||||||
|     if (position !== -1) { |     if (position !== -1) { | ||||||
|             newDetails.splice(position, 1) |       newDetails.splice(position, 1); | ||||||
|         } |     } else { | ||||||
|         else { |       newDetails = [...details, index]; | ||||||
|             newDetails = [...details, index] |  | ||||||
|         } |  | ||||||
|         setDetails(newDetails) |  | ||||||
|     } |     } | ||||||
|  |     setDetails(newDetails); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   const columns = [ |   const columns = [ | ||||||
|     { key: 'UUID', label: 'Config. Id' }, |     { key: 'UUID', label: 'Config. Id' }, | ||||||
| @@ -70,23 +61,19 @@ const DeviceHealth = () => { | |||||||
|       label: '', |       label: '', | ||||||
|       _style: { width: '1%' }, |       _style: { width: '1%' }, | ||||||
|       sorter: false, |       sorter: false, | ||||||
|             filter: false |       filter: false, | ||||||
|           } |     }, | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|         setLoading(true); |  | ||||||
|     getDeviceHealth(); |     getDeviceHealth(); | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|   if (healthChecks && healthChecks.length > 0) { |   if (healthChecks && healthChecks.length > 0) { | ||||||
|     sanityLevel = healthChecks[0].sanity; |     sanityLevel = healthChecks[0].sanity; | ||||||
|         if(sanityLevel === 100) |     if (sanityLevel === 100) barColor = 'gradient-success'; | ||||||
|             barColor = "gradient-success"; |     else if (sanityLevel >= 90) barColor = 'gradient-warning'; | ||||||
|         else if (sanityLevel >= 90) |     else barColor = 'gradient-danger'; | ||||||
|             barColor = "gradient-warning"; |  | ||||||
|         else |  | ||||||
|             barColor = "gradient-danger"; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| @@ -107,22 +94,11 @@ const DeviceHealth = () => { | |||||||
|                   style={{ color: 'white' }} |                   style={{ color: 'white' }} | ||||||
|                   sorterValue={{ column: 'recorded', desc: 'true' }} |                   sorterValue={{ column: 'recorded', desc: 'true' }} | ||||||
|                   scopedSlots={{ |                   scopedSlots={{ | ||||||
|                                     'recorded': |                     recorded: (item) => <td>{cleanTimestamp(item.recorded)}</td>, | ||||||
|                                     (item)=>( |                     sanity: (item) => <td>{`${item.sanity}%`}</td>, | ||||||
|                                         <td> |                     show_details: (item, index) => { | ||||||
|                                         {cleanTimestamp(item.recorded)} |  | ||||||
|                                         </td> |  | ||||||
|                                     ), |  | ||||||
|                                     'sanity': |  | ||||||
|                                     (item)=>( |  | ||||||
|                                         <td> |  | ||||||
|                                             {`${item.sanity}%`} |  | ||||||
|                                         </td> |  | ||||||
|                                     ), |  | ||||||
|                                     'show_details': |  | ||||||
|                                     (item, index)=>{ |  | ||||||
|                       if (item.sanity === 100) { |                       if (item.sanity === 100) { | ||||||
|                                             return (<></>); |                         return <></>; | ||||||
|                       } |                       } | ||||||
|                       return ( |                       return ( | ||||||
|                         <td className="py-2"> |                         <td className="py-2"> | ||||||
| @@ -131,38 +107,41 @@ const DeviceHealth = () => { | |||||||
|                             variant="outline" |                             variant="outline" | ||||||
|                             shape="square" |                             shape="square" | ||||||
|                             size="sm" |                             size="sm" | ||||||
|                                                 onClick={()=>{toggleDetails(index)}} |                             onClick={() => { | ||||||
|  |                               toggleDetails(index); | ||||||
|  |                             }} | ||||||
|                           > |                           > | ||||||
|                             {details.includes(index) ? 'Hide' : 'Show'} |                             {details.includes(index) ? 'Hide' : 'Show'} | ||||||
|                           </CButton> |                           </CButton> | ||||||
|                         </td> |                         </td> | ||||||
|                       ); |                       ); | ||||||
|                     }, |                     }, | ||||||
|                                     'details': |                     details: (item, index) => ( | ||||||
|                                         (item, index)=>{ |  | ||||||
|                                             return ( |  | ||||||
|                       <CCollapse show={details.includes(index)}> |                       <CCollapse show={details.includes(index)}> | ||||||
|                         <CCardBody> |                         <CCardBody> | ||||||
|                           <h5>Details</h5> |                           <h5>Details</h5> | ||||||
|                                                         <div><pre>{JSON.stringify(item.values, null, 4)}</pre></div> |                           <div> | ||||||
|  |                             <pre>{JSON.stringify(item.values, null, 4)}</pre> | ||||||
|  |                           </div> | ||||||
|                         </CCardBody> |                         </CCardBody> | ||||||
|                       </CCollapse> |                       </CCollapse> | ||||||
|                                             ); |                     ), | ||||||
|                                     } |  | ||||||
|                   }} |                   }} | ||||||
|                             > |                 /> | ||||||
|                             </CDataTable> |  | ||||||
|               </div> |               </div> | ||||||
|             </CCard> |             </CCard> | ||||||
|           </CCollapse> |           </CCollapse> | ||||||
|           <CButton show={collapse} color="transparent" onClick={toggle} block> |           <CButton show={collapse} color="transparent" onClick={toggle} block> | ||||||
|                         <CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} style={{color: 'white'}} size="lg"/> |             <CIcon | ||||||
|  |               name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} | ||||||
|  |               style={{ color: 'white' }} | ||||||
|  |               size="lg" | ||||||
|  |             /> | ||||||
|           </CButton> |           </CButton> | ||||||
|         </div> |         </div> | ||||||
|       } |       } | ||||||
|         > |     /> | ||||||
|         </CWidgetProgress> |  | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default DeviceHealth | export default DeviceHealth; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useEffect, useState } from 'react' | import React, { useEffect, useState } from 'react'; | ||||||
| import { | import { | ||||||
|   CBadge, |   CBadge, | ||||||
|   CCardBody, |   CCardBody, | ||||||
| @@ -9,17 +9,17 @@ import { | |||||||
|   CCardHeader, |   CCardHeader, | ||||||
|   CRow, |   CRow, | ||||||
|   CCol, |   CCol, | ||||||
| 	CPopover |   CPopover, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
| import ReactPaginate from 'react-paginate'; | import ReactPaginate from 'react-paginate'; | ||||||
| import Select from 'react-select' | import Select from 'react-select'; | ||||||
|  | import { cilSync, cilInfo } from '@coreui/icons'; | ||||||
|  | import CIcon from '@coreui/icons-react'; | ||||||
| import { getToken } from '../utils/authHelper'; | import { getToken } from '../utils/authHelper'; | ||||||
| import axiosInstance from '../utils/axiosInstance'; | import axiosInstance from '../utils/axiosInstance'; | ||||||
| import { cleanBytesString, cropStringWithEllipsis } from '../utils/helper'; | import { cleanBytesString, cropStringWithEllipsis } from '../utils/helper'; | ||||||
| import iotIcon from '../assets/icons/iot.png' | import iotIcon from '../assets/icons/iot.png'; | ||||||
| import internetSwitch from '../assets/icons/networkswitch.png'; | import internetSwitch from '../assets/icons/networkswitch.png'; | ||||||
| import {  cilSync, cilInfo } from '@coreui/icons'; |  | ||||||
| import CIcon from '@coreui/icons-react'; |  | ||||||
|  |  | ||||||
| const DeviceList = () => { | const DeviceList = () => { | ||||||
|   const [loadedSerials, setLoadedSerials] = useState(false); |   const [loadedSerials, setLoadedSerials] = useState(false); | ||||||
| @@ -28,7 +28,6 @@ const DeviceList = () => { | |||||||
|   const [pageCount, setPageCount] = useState(0); |   const [pageCount, setPageCount] = useState(0); | ||||||
|   const [devicesPerPage, setDevicesPerPage] = useState(10); |   const [devicesPerPage, setDevicesPerPage] = useState(10); | ||||||
|   const [devices, setDevices] = useState([]); |   const [devices, setDevices] = useState([]); | ||||||
| 	const [details, setDetails] = useState([]); |  | ||||||
|   const [loading, setLoading] = useState(true); |   const [loading, setLoading] = useState(true); | ||||||
|  |  | ||||||
|   const getSerialNumbers = () => { |   const getSerialNumbers = () => { | ||||||
| @@ -36,18 +35,19 @@ const DeviceList = () => { | |||||||
|     setLoading(true); |     setLoading(true); | ||||||
|  |  | ||||||
|     const headers = { |     const headers = { | ||||||
| 			'Accept': 'application/json', |       Accept: 'application/json', | ||||||
| 			'Authorization': `Bearer ${token}` |       Authorization: `Bearer ${token}`, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| 		axiosInstance.get('/devices?serialOnly=true', { |     axiosInstance | ||||||
| 			headers: headers |       .get('/devices?serialOnly=true', { | ||||||
|  |         headers, | ||||||
|       }) |       }) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setSerialNumbers(response.data.serialNumbers); |         setSerialNumbers(response.data.serialNumbers); | ||||||
|         setLoadedSerials(true); |         setLoadedSerials(true); | ||||||
|       }) |       }) | ||||||
| 		.catch(error => { |       .catch((error) => { | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
|       }); |       }); | ||||||
| @@ -58,46 +58,35 @@ const DeviceList = () => { | |||||||
|     setLoading(true); |     setLoading(true); | ||||||
|  |  | ||||||
|     const headers = { |     const headers = { | ||||||
| 			'Accept': 'application/json', |       Accept: 'application/json', | ||||||
| 			'Authorization': `Bearer ${token}` |       Authorization: `Bearer ${token}`, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     const startIndex = page * devicesPerPage; |     const startIndex = page * devicesPerPage; | ||||||
| 		const endIndex = parseInt(startIndex) + parseInt(devicesPerPage); |     const endIndex = parseInt(startIndex, 10) + parseInt(devicesPerPage, 10); | ||||||
|     const serialsToGet = serialNumbers.slice(startIndex, endIndex).join(','); |     const serialsToGet = serialNumbers.slice(startIndex, endIndex).join(','); | ||||||
|  |  | ||||||
| 		axiosInstance.get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, { |     axiosInstance | ||||||
| 			headers: headers |       .get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, { | ||||||
|  |         headers, | ||||||
|       }) |       }) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setDevices(response.data.devicesWithStatus); |         setDevices(response.data.devicesWithStatus); | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
|       }) |       }) | ||||||
| 		.catch(error => { |       .catch((error) => { | ||||||
|         setLoading(false); |         setLoading(false); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
|       }); |       }); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
| 	//Function called from the button on the table so that a user can see more details |  | ||||||
| 	const toggleDetails = (index) => { |  | ||||||
| 		const position = details.indexOf(index) |  | ||||||
| 		let newDetails = details.slice() |  | ||||||
| 		if (position !== -1) { |  | ||||||
| 		newDetails.splice(position, 1) |  | ||||||
| 		} else { |  | ||||||
| 		newDetails = [...details, index] |  | ||||||
| 		} |  | ||||||
| 		setDetails(newDetails) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|   const updateDevicesPerPage = (value) => { |   const updateDevicesPerPage = (value) => { | ||||||
|     setDevicesPerPage(value); |     setDevicesPerPage(value); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
|   const updatePageCount = ({ selected: selectedPage }) => { |   const updatePageCount = ({ selected: selectedPage }) => { | ||||||
|     setPage(selectedPage); |     setPage(selectedPage); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
|   // Initial load |   // Initial load | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
| @@ -119,8 +108,6 @@ const DeviceList = () => { | |||||||
|   return ( |   return ( | ||||||
|     <DeviceListDisplay |     <DeviceListDisplay | ||||||
|       devices={devices} |       devices={devices} | ||||||
| 			toggleDetails={toggleDetails}  |  | ||||||
| 			details={details} |  | ||||||
|       loading={loading} |       loading={loading} | ||||||
|       updateDevicesPerPage={updateDevicesPerPage} |       updateDevicesPerPage={updateDevicesPerPage} | ||||||
|       pageCount={pageCount} |       pageCount={pageCount} | ||||||
| @@ -128,15 +115,15 @@ const DeviceList = () => { | |||||||
|       pageRangeDisplayed={5} |       pageRangeDisplayed={5} | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDevicesPerPage, pageCount, updatePage }) => { | const DeviceListDisplay = ({ devices, loading, updateDevicesPerPage, pageCount, updatePage }) => { | ||||||
|   const columns = [ |   const columns = [ | ||||||
| 	{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' },}, |     { key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' } }, | ||||||
|     { key: 'serialNumber', _style: { width: '5%' } }, |     { key: 'serialNumber', _style: { width: '5%' } }, | ||||||
|     { key: 'UUID', label: 'Config Id', _style: { width: '5%' } }, |     { key: 'UUID', label: 'Config Id', _style: { width: '5%' } }, | ||||||
| 	{ key: 'firmware', filter: false, _style: { width: '20%' }, }, |     { key: 'firmware', filter: false, _style: { width: '20%' } }, | ||||||
| 	{ key: 'manufacturer', filter: false, _style: { width: '20%' }, }, |     { key: 'manufacturer', filter: false, _style: { width: '20%' } }, | ||||||
|     { key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } }, |     { key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } }, | ||||||
|     { key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } }, |     { key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } }, | ||||||
|     { key: 'ipAddress', _style: { width: '20%' } }, |     { key: 'ipAddress', _style: { width: '20%' } }, | ||||||
| @@ -145,57 +132,55 @@ const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDev | |||||||
|       label: '', |       label: '', | ||||||
|       _style: { width: '3%' }, |       _style: { width: '3%' }, | ||||||
|       sorter: false, |       sorter: false, | ||||||
| 	  filter: false |       filter: false, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       key: 'refresh', |       key: 'refresh', | ||||||
|       label: '', |       label: '', | ||||||
|       _style: { width: '2%' }, |       _style: { width: '2%' }, | ||||||
|       sorter: false, |       sorter: false, | ||||||
| 		filter: false |       filter: false, | ||||||
| 	  } |     }, | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|   const selectOptions = [ |   const selectOptions = [ | ||||||
|     { value: '10', label: '10' }, |     { value: '10', label: '10' }, | ||||||
|     { value: '25', label: '25' }, |     { value: '25', label: '25' }, | ||||||
| 	{ value: '50', label: '50' } |     { value: '50', label: '50' }, | ||||||
|   ] |   ]; | ||||||
|  |  | ||||||
|  |  | ||||||
|   const getDeviceIcon = (deviceType) => { |   const getDeviceIcon = (deviceType) => { | ||||||
| 	if(deviceType === "AP_Default"){ |     if (deviceType === 'AP_Default') { | ||||||
| 	  return <CIcon name="cilRouter" size="2xl" alt='AP'></CIcon>; |       return <CIcon name="cilRouter" size="2xl" alt="AP" />; | ||||||
|     } |     } | ||||||
| 	else if(deviceType === "IOT"){ |     if (deviceType === 'IOT') { | ||||||
| 	  return <img src={iotIcon} style={{height:'32px', width:'32px'}} alt='IOT'/>; |       return <img src={iotIcon} style={{ height: '32px', width: '32px' }} alt="IOT" />; | ||||||
|     } |     } | ||||||
| 	else if(deviceType === "SWITCH"){ |     if (deviceType === 'SWITCH') { | ||||||
| 	  return <img src={internetSwitch} style={{height:'32px', width:'32px'}} alt='SWITCH'/>; |       return <img src={internetSwitch} style={{ height: '32px', width: '32px' }} alt="SWITCH" />; | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|   } |   }; | ||||||
|  |  | ||||||
|   const getStatusBadge = (status) => { |   const getStatusBadge = (status) => { | ||||||
|     if (status) { |     if (status) { | ||||||
|       return 'success'; |       return 'success'; | ||||||
|     } |     } | ||||||
|     return 'danger'; |     return 'danger'; | ||||||
|   } |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <CCard> |       <CCard> | ||||||
|         <CCardHeader> |         <CCardHeader> | ||||||
|           <CRow> |           <CRow> | ||||||
| 				<CCol> |             <CCol /> | ||||||
| 				</CCol> |  | ||||||
|             <CCol xs={2}> |             <CCol xs={2}> | ||||||
|               <Select |               <Select | ||||||
|                 isClearable={false} |                 isClearable={false} | ||||||
|                 options={selectOptions} |                 options={selectOptions} | ||||||
|                 defaultValue={{ value: '10', label: '10' }} |                 defaultValue={{ value: '10', label: '10' }} | ||||||
| 						onChange={value => updateDevicesPerPage(value.value)} |                 onChange={(value) => updateDevicesPerPage(value.value)} | ||||||
|               /> |               /> | ||||||
|             </CCol> |             </CCol> | ||||||
|           </CRow> |           </CRow> | ||||||
| @@ -208,8 +193,7 @@ const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDev | |||||||
|             hover |             hover | ||||||
|             loading={loading} |             loading={loading} | ||||||
|             scopedSlots={{ |             scopedSlots={{ | ||||||
| 				'deviceType': |               deviceType: (item) => ( | ||||||
| 				(item)=>( |  | ||||||
|                 <td style={{ textAlign: 'center' }}> |                 <td style={{ textAlign: 'center' }}> | ||||||
|                   <CPopover |                   <CPopover | ||||||
|                     content={item.connected ? 'Connected' : 'Not Connected'} |                     content={item.connected ? 'Connected' : 'Not Connected'} | ||||||
| @@ -221,102 +205,66 @@ const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDev | |||||||
|                   </CPopover> |                   </CPopover> | ||||||
|                 </td> |                 </td> | ||||||
|               ), |               ), | ||||||
| 				'firmware': |               firmware: (item) => ( | ||||||
| 				(item)=>( |  | ||||||
|                 <td> |                 <td> | ||||||
| 						<CPopover |                   <CPopover content={item.firmware ? item.firmware : 'N/A'} placement="top"> | ||||||
| 							content={ item.firmware ? item.firmware : 'N/A' } |                     <p>{cropStringWithEllipsis(item.firmware, 22)}</p> | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<p> |  | ||||||
| 								{cropStringWithEllipsis(item.firmware, 22)} |  | ||||||
| 							</p> |  | ||||||
|                   </CPopover> |                   </CPopover> | ||||||
|                 </td> |                 </td> | ||||||
|               ), |               ), | ||||||
| 				'manufacturer': |               manufacturer: (item) => ( | ||||||
| 				(item)=>( |  | ||||||
|                 <td> |                 <td> | ||||||
| 						<CPopover |                   <CPopover content={item.manufacturer ? item.manufacturer : 'N/A'} placement="top"> | ||||||
| 							content={ item.manufacturer ? item.manufacturer : 'N/A' } |                     <p>{cropStringWithEllipsis(item.manufacturer, 23)}</p> | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<p> |  | ||||||
| 								{cropStringWithEllipsis(item.manufacturer, 23)} |  | ||||||
| 							</p> |  | ||||||
|                   </CPopover> |                   </CPopover> | ||||||
|                 </td> |                 </td> | ||||||
|               ), |               ), | ||||||
| 				'txBytes': |               txBytes: (item) => <td>{cleanBytesString(item.txBytes)}</td>, | ||||||
| 				(item)=>( |               rxBytes: (item) => <td>{cleanBytesString(item.rxBytes)}</td>, | ||||||
|  |               ipAddress: (item) => ( | ||||||
|                 <td> |                 <td> | ||||||
| 					{cleanBytesString(item.txBytes)} |                   <CPopover content={item.ipAddress ? item.ipAddress : 'N/A'} placement="top"> | ||||||
| 					</td> |                     <p>{cropStringWithEllipsis(item.ipAddress, 22)}</p> | ||||||
| 				), |  | ||||||
| 				'rxBytes': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td> |  | ||||||
| 					{cleanBytesString(item.rxBytes)} |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'ipAddress': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td> |  | ||||||
| 						<CPopover |  | ||||||
| 							content={ item.ipAddress ? item.ipAddress : 'N/A' } |  | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<p> |  | ||||||
| 								{cropStringWithEllipsis(item.ipAddress, 22)} |  | ||||||
| 							</p> |  | ||||||
|                   </CPopover> |                   </CPopover> | ||||||
|                 </td> |                 </td> | ||||||
|               ), |               ), | ||||||
| 				'refresh': |               refresh: () => ( | ||||||
| 				(item)=>( |  | ||||||
|                 <td className="py-2"> |                 <td className="py-2"> | ||||||
|                   <CButton color="primary" variant="outline" size="sm"> |                   <CButton color="primary" variant="outline" size="sm"> | ||||||
|                     <CIcon name="cil-sync" content={cilSync} size="sm" /> |                     <CIcon name="cil-sync" content={cilSync} size="sm" /> | ||||||
|                   </CButton> |                   </CButton> | ||||||
|                 </td> |                 </td> | ||||||
|               ), |               ), | ||||||
| 				'show_details': |               show_details: (item) => ( | ||||||
| 				(item, index)=>{ |  | ||||||
| 					return ( |  | ||||||
|                 <td className="py-2"> |                 <td className="py-2"> | ||||||
|                   <CLink |                   <CLink | ||||||
|                     className="c-subheader-nav-link" |                     className="c-subheader-nav-link" | ||||||
|                     aria-current="page" |                     aria-current="page" | ||||||
|                     to={() => `/devices/${item.serialNumber}`} |                     to={() => `/devices/${item.serialNumber}`} | ||||||
|                   > |                   > | ||||||
| 							<CButton |                     <CButton color="primary" variant="outline" shape="square" size="sm"> | ||||||
| 								color="primary" |  | ||||||
| 								variant="outline" |  | ||||||
| 								shape="square" |  | ||||||
| 								size="sm" |  | ||||||
| 							> |  | ||||||
|                       <CIcon name="cil-info" content={cilInfo} size="l" /> |                       <CIcon name="cil-info" content={cilInfo} size="l" /> | ||||||
|                     </CButton> |                     </CButton> | ||||||
|                   </CLink> |                   </CLink> | ||||||
|                 </td> |                 </td> | ||||||
| 					) |               ), | ||||||
| 				} |             }} | ||||||
| 			}}/> |           /> | ||||||
|           <ReactPaginate |           <ReactPaginate | ||||||
| 				previousLabel={"← Previous"} |             previousLabel="← Previous" | ||||||
| 				nextLabel={"Next →"} |             nextLabel="Next →" | ||||||
|             pageCount={pageCount} |             pageCount={pageCount} | ||||||
|             onPageChange={updatePage} |             onPageChange={updatePage} | ||||||
| 				breakClassName={'page-item'} |             breakClassName="page-item" | ||||||
| 				breakLinkClassName={'page-link'} |             breakLinkClassName="page-link" | ||||||
| 				containerClassName={'pagination'} |             containerClassName="pagination" | ||||||
| 				pageClassName={'page-item'} |             pageClassName="page-item" | ||||||
| 				pageLinkClassName={'page-link'} |             pageLinkClassName="page-link" | ||||||
| 				previousClassName={'page-item'} |             previousClassName="page-item" | ||||||
| 				previousLinkClassName={'page-link'} |             previousLinkClassName="page-link" | ||||||
| 				nextClassName={'page-item'} |             nextClassName="page-item" | ||||||
| 				nextLinkClassName={'page-link'} |             nextLinkClassName="page-link" | ||||||
| 				activeClassName={'active'} |             activeClassName="active" | ||||||
|           /> |           /> | ||||||
|         </CCardBody> |         </CCardBody> | ||||||
|       </CCard> |       </CCard> | ||||||
| @@ -324,5 +272,4 @@ const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDev | |||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| export default DeviceList; | export default DeviceList; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { | import { | ||||||
|   CCard, |   CCard, | ||||||
|   CCardHeader, |   CCardHeader, | ||||||
| @@ -10,15 +10,15 @@ import { | |||||||
|   CInput, |   CInput, | ||||||
|   CCollapse, |   CCollapse, | ||||||
|   CCardFooter, |   CCardFooter, | ||||||
|     CButton |   CButton, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
|  |  | ||||||
| const DeviceStatus = () => { | const DeviceStatus = () => { | ||||||
|     let device = useSelector(state => state.selectedDevice); |   const device = useSelector((state) => state.selectedDevice); | ||||||
|  |  | ||||||
|   if (device) { |   if (device) { | ||||||
|         let barColor = device.connected ? 'gradient-success' : 'gradient-warning'; |     const barColor = device.connected ? 'gradient-success' : 'gradient-warning'; | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <CWidgetProgressIcon |       <CWidgetProgressIcon | ||||||
| @@ -42,13 +42,10 @@ const DeviceStatus = () => { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|      |  | ||||||
|   if (device) { |   if (device) { | ||||||
|     return ( |     return ( | ||||||
|       <CCard> |       <CCard> | ||||||
|                 <CCardHeader> |         <CCardHeader>Device #{device.serialNumber} Configuration</CCardHeader> | ||||||
|                     Device #{device.serialNumber} Configuration |  | ||||||
|                 </CCardHeader> |  | ||||||
|         <CCardBody> |         <CCardBody> | ||||||
|           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> |           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> | ||||||
|             <CFormGroup row> |             <CFormGroup row> | ||||||
| @@ -142,11 +139,9 @@ const DeviceStatus = () => { | |||||||
|               </CFormGroup> |               </CFormGroup> | ||||||
|             </CCollapse> |             </CCollapse> | ||||||
|             <CCardFooter> |             <CCardFooter> | ||||||
|                             <CButton |               <CButton color="primary" onClick={toggle} className="mb-1"> | ||||||
|                             color="primary" |                 More details | ||||||
|                             onClick={toggle} |               </CButton> | ||||||
|                             className={'mb-1'} |  | ||||||
|                             >More details</CButton> |  | ||||||
|             </CCardFooter> |             </CCardFooter> | ||||||
|           </CForm> |           </CForm> | ||||||
|         </CCardBody> |         </CCardBody> | ||||||
| @@ -156,13 +151,10 @@ const DeviceStatus = () => { | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CCard> |     <CCard> | ||||||
|                 <CCardHeader> |       <CCardHeader>Device Configuration</CCardHeader> | ||||||
|                     Device Configuration |       <CCardBody /> | ||||||
|                 </CCardHeader> |  | ||||||
|                 <CCardBody> |  | ||||||
|                 </CCardBody> |  | ||||||
|     </CCard> |     </CCard> | ||||||
|   ); |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default DeviceStatus; | export default DeviceStatus; | ||||||
| @@ -10,13 +10,13 @@ import { | |||||||
|   CCol, |   CCol, | ||||||
|   CRow, |   CRow, | ||||||
|   CInput, |   CInput, | ||||||
|     CInvalidFeedback |   CInvalidFeedback, | ||||||
| } from '@coreui/react'; | } from '@coreui/react'; | ||||||
| import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||||
| import { convertDateToUtc, convertDateFromUtc } from '../utils/helper' | import DatePicker from 'react-widgets/DatePicker'; | ||||||
| import DatePicker from "react-widgets/DatePicker"; |  | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
| import "react-widgets/styles.css"; | import { convertDateToUtc, convertDateFromUtc } from '../utils/helper'; | ||||||
|  | import 'react-widgets/styles.css'; | ||||||
| import { getToken } from '../utils/authHelper'; | import { getToken } from '../utils/authHelper'; | ||||||
| import axiosInstance from '../utils/axiosInstance'; | import axiosInstance from '../utils/axiosInstance'; | ||||||
|  |  | ||||||
| @@ -29,20 +29,20 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|   const [validFirmware, setValidFirmware] = useState(true); |   const [validFirmware, setValidFirmware] = useState(true); | ||||||
|   const [responseBody, setResponseBody] = useState(''); |   const [responseBody, setResponseBody] = useState(''); | ||||||
|   const [checkingIfSure, setCheckingIfSure] = useState(false); |   const [checkingIfSure, setCheckingIfSure] = useState(false); | ||||||
|     const selectedDeviceId = useSelector(state => state.selectedDeviceId); |   const selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|  |  | ||||||
|   const formValidation = () => { |   const formValidation = () => { | ||||||
|     if (firmware.trim() === '') { |     if (firmware.trim() === '') { | ||||||
|       setValidFirmware(false); |       setValidFirmware(false); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|         return (chosenDate !== null); |     return chosenDate !== null; | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const setDateToNow = () => { |   const setDateToNow = () => { | ||||||
|         const now = (new Date()).toISOString(); |     const now = new Date().toISOString(); | ||||||
|     setChosenDate(now); |     setChosenDate(now); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const setDateToLate = () => { |   const setDateToLate = () => { | ||||||
|     const date = convertDateToUtc(new Date()); |     const date = convertDateToUtc(new Date()); | ||||||
| @@ -53,17 +53,17 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|     date.setMinutes(0); |     date.setMinutes(0); | ||||||
|  |  | ||||||
|     setChosenDate(convertDateFromUtc(date).toISOString()); |     setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const setDate = (date) => { |   const setDate = (date) => { | ||||||
|     if (date) { |     if (date) { | ||||||
|       setChosenDate(date.toISOString()); |       setChosenDate(date.toISOString()); | ||||||
|     } |     } | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const confirmingIfSure = () => { |   const confirmingIfSure = () => { | ||||||
|     setCheckingIfSure(true); |     setCheckingIfSure(true); | ||||||
|     } |   }; | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     setHadSuccess(false); |     setHadSuccess(false); | ||||||
|     setHadFailure(false); |     setHadFailure(false); | ||||||
| @@ -87,22 +87,23 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|     const token = getToken(); |     const token = getToken(); | ||||||
|  |  | ||||||
|     const headers = { |     const headers = { | ||||||
| 			'Accept': 'application/json', |       Accept: 'application/json', | ||||||
| 			'Authorization': `Bearer ${token}`, |       Authorization: `Bearer ${token}`, | ||||||
| 			'serialNumber': selectedDeviceId |       serialNumber: selectedDeviceId, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     const parameters = { |     const parameters = { | ||||||
|       serialNumber: selectedDeviceId, |       serialNumber: selectedDeviceId, | ||||||
|       when: chosenDate, |       when: chosenDate, | ||||||
|             uri: firmware |       uri: firmware, | ||||||
|         } |     }; | ||||||
| 		axiosInstance.post(`/device/${selectedDeviceId}/upgrade`, parameters,{ headers: headers}) |     axiosInstance | ||||||
|  |       .post(`/device/${selectedDeviceId}/upgrade`, parameters, { headers }) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setResponseBody(JSON.stringify(response.data, null, 4)); |         setResponseBody(JSON.stringify(response.data, null, 4)); | ||||||
|         setHadSuccess(true); |         setHadSuccess(true); | ||||||
|       }) |       }) | ||||||
| 		.catch(error => { |       .catch((error) => { | ||||||
|         setHadFailure(true); |         setHadFailure(true); | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
| @@ -111,13 +112,10 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|         setCheckingIfSure(false); |         setCheckingIfSure(false); | ||||||
|         setWaiting(false); |         setWaiting(false); | ||||||
|       }); |       }); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|         <CModal  |     <CModal show={show} onClose={toggleModal}> | ||||||
|             show={show}  |  | ||||||
|             onClose={toggleModal} |  | ||||||
|         > |  | ||||||
|       <CModalHeader closeButton> |       <CModalHeader closeButton> | ||||||
|         <CModalTitle>Firmware Upgrade</CModalTitle> |         <CModalTitle>Firmware Upgrade</CModalTitle> | ||||||
|       </CModalHeader> |       </CModalHeader> | ||||||
| @@ -125,10 +123,14 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|         <h6>Choose a time and a firmware version for this device</h6> |         <h6>Choose a time and a firmware version for this device</h6> | ||||||
|         <CRow style={{ marginTop: '20px' }}> |         <CRow style={{ marginTop: '20px' }}> | ||||||
|           <CCol> |           <CCol> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton> |             <CButton block color="primary" onClick={() => setDateToNow()}> | ||||||
|  |               Now | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|           <CCol> |           <CCol> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>  |             <CButton block color="primary" onClick={() => setDateToLate()}> | ||||||
|  |               Later tonight | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|         <CRow style={{ marginTop: '20px' }}> |         <CRow style={{ marginTop: '20px' }}> | ||||||
| @@ -140,41 +142,43 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|               selected={Date.parse(chosenDate)} |               selected={Date.parse(chosenDate)} | ||||||
|               includeTime |               includeTime | ||||||
|               selectTime |               selectTime | ||||||
|                             placeholder = 'Select custom date in UTC' |               placeholder="Select custom date in UTC" | ||||||
|               disabled={waiting} |               disabled={waiting} | ||||||
|                             onChange = {date => setDate(date)}   |               onChange={(date) => setDate(date)} | ||||||
|               min={new Date()} |               min={new Date()} | ||||||
|             /> |             /> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|  |  | ||||||
|         <div style={{ marginTop: '25px' }}> |         <div style={{ marginTop: '25px' }}> | ||||||
|                     <p>Device will upgrade at (UTC): <b>{ chosenDate }</b></p> |           <p> | ||||||
|  |             Device will upgrade at (UTC): <b>{chosenDate}</b> | ||||||
|  |           </p> | ||||||
|         </div> |         </div> | ||||||
|         <CInput |         <CInput | ||||||
|           disabled={waiting} |           disabled={waiting} | ||||||
|                     className = {'form-control', {'is-invalid' : !validFirmware}}  |           className={('form-control', { 'is-invalid': !validFirmware })} | ||||||
|           type="text" |           type="text" | ||||||
|           id="uri" |           id="uri" | ||||||
|           name="uri-input" |           name="uri-input" | ||||||
|           placeholder="https://somelocation.com/file=newversion.bin" |           placeholder="https://somelocation.com/file=newversion.bin" | ||||||
|           autoComplete="firmware-uri" |           autoComplete="firmware-uri" | ||||||
|                     onChange={event => setFirmware(event.target.value)} |           onChange={(event) => setFirmware(event.target.value)} | ||||||
|           value={firmware} |           value={firmware} | ||||||
|         /> |         /> | ||||||
|         <CInvalidFeedback>You need a url...</CInvalidFeedback> |         <CInvalidFeedback>You need a url...</CInvalidFeedback> | ||||||
|                 <div hidden={(!hadSuccess && !hadFailure)}> |         <div hidden={!hadSuccess && !hadFailure}> | ||||||
|                     <div><pre>{responseBody}</pre></div> |           <div> | ||||||
|  |             <pre>{responseBody}</pre> | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </CModalBody> |       </CModalBody> | ||||||
|       <CModalFooter> |       <CModalFooter> | ||||||
|                 <div hidden= { !checkingIfSure }> |         <div hidden={!checkingIfSure}>Are you sure?</div> | ||||||
|                     Are you sure? |  | ||||||
|                 </div> |  | ||||||
|         <CButton |         <CButton | ||||||
|           hidden={checkingIfSure} |           hidden={checkingIfSure} | ||||||
|           color="primary" |           color="primary" | ||||||
|                     onClick={event => formValidation() ? confirmingIfSure() : null} |           onClick={() => (formValidation() ? confirmingIfSure() : null)} | ||||||
|         > |         > | ||||||
|           Schedule Upgrade |           Schedule Upgrade | ||||||
|         </CButton> |         </CButton> | ||||||
| @@ -182,28 +186,23 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => { | |||||||
|           hidden={!checkingIfSure} |           hidden={!checkingIfSure} | ||||||
|           disabled={waiting} |           disabled={waiting} | ||||||
|           color="primary" |           color="primary" | ||||||
|                     onClick={event => formValidation() ? postUpgrade() : null} |           onClick={() => (formValidation() ? postUpgrade() : null)} | ||||||
|         > |         > | ||||||
|           {waiting ? 'Loading...' : 'Yes'} {'   '} |           {waiting ? 'Loading...' : 'Yes'} {'   '} | ||||||
|           <CSpinner hidden={!waiting} component="span" size="sm" /> |           <CSpinner hidden={!waiting} component="span" size="sm" /> | ||||||
|                     <CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill"> |           <CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill"> | ||||||
|             Success |             Success | ||||||
|           </CBadge> |           </CBadge> | ||||||
|                     <CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill"> |           <CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill"> | ||||||
|             Request Failed |             Request Failed | ||||||
|           </CBadge> |           </CBadge> | ||||||
|         </CButton> |         </CButton> | ||||||
|                 <CButton  |         <CButton color="secondary" onClick={toggleModal}> | ||||||
|                     color="secondary"  |  | ||||||
|                     onClick={ toggleModal } |  | ||||||
|                 > |  | ||||||
|           Cancel |           Cancel | ||||||
|         </CButton> |         </CButton> | ||||||
|       </CModalFooter> |       </CModalFooter> | ||||||
|              |  | ||||||
|     </CModal> |     </CModal> | ||||||
|   ); |   ); | ||||||
|      | }; | ||||||
| } |  | ||||||
|  |  | ||||||
| export default FirmwareUpgradeModal; | export default FirmwareUpgradeModal; | ||||||
| @@ -1,45 +1,42 @@ | |||||||
| import React, { Suspense } from 'react' | /* eslint-disable react/jsx-props-no-spreading */ | ||||||
| import { | import React, { Suspense } from 'react'; | ||||||
|   Redirect, | import { Redirect, Route, Switch } from 'react-router-dom'; | ||||||
|   Route, | import { v4 as createUuid } from 'uuid'; | ||||||
|   Switch | import { CContainer, CFade } from '@coreui/react'; | ||||||
| } from 'react-router-dom' | import routes from '../routes'; | ||||||
| import { CContainer, CFade } from '@coreui/react' |  | ||||||
| import routes from '../routes' |  | ||||||
|  |  | ||||||
| const loading = ( | const loading = ( | ||||||
|   <div className="pt-3 text-center"> |   <div className="pt-3 text-center"> | ||||||
|     <div className="sk-spinner sk-spinner-pulse"></div> |     <div className="sk-spinner sk-spinner-pulse" /> | ||||||
|   </div> |   </div> | ||||||
| ) | ); | ||||||
|  |  | ||||||
| const TheContent = () => { | const TheContent = () => ( | ||||||
|   return ( |  | ||||||
|   <main className="c-main"> |   <main className="c-main"> | ||||||
|     <CContainer fluid> |     <CContainer fluid> | ||||||
|       <Suspense fallback={loading}> |       <Suspense fallback={loading}> | ||||||
|         <Switch> |         <Switch> | ||||||
|             { |           {routes.map( | ||||||
|             routes.map((route, idx) => { |             (route) => | ||||||
|               return route.component && ( |               route.component && ( | ||||||
|                 <Route |                 <Route | ||||||
|                   key={idx} |                   key={createUuid()} | ||||||
|                   path={route.path} |                   path={route.path} | ||||||
|                   exact={route.exact} |                   exact={route.exact} | ||||||
|                   name={route.name} |                   name={route.name} | ||||||
|                   render={props => ( |                   render={(props) => ( | ||||||
|                     <CFade> |                     <CFade> | ||||||
|                       <route.component {...props} /> |                       <route.component {...props} /> | ||||||
|                     </CFade> |                     </CFade> | ||||||
|                   )} /> |                   )} | ||||||
|               ) |                 /> | ||||||
|             })} |               ), | ||||||
|  |           )} | ||||||
|           <Redirect from="/" to="/devices" /> |           <Redirect from="/" to="/devices" /> | ||||||
|         </Switch> |         </Switch> | ||||||
|       </Suspense> |       </Suspense> | ||||||
|     </CContainer> |     </CContainer> | ||||||
|   </main> |   </main> | ||||||
|   ) | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default React.memo(TheContent) | export default React.memo(TheContent); | ||||||
|   | |||||||
| @@ -1,19 +1,21 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { CFooter } from '@coreui/react' | import { CFooter } from '@coreui/react'; | ||||||
|  |  | ||||||
| const TheFooter = () => { | const TheFooter = () => ( | ||||||
|   return ( |  | ||||||
|   <CFooter fixed={false}> |   <CFooter fixed={false}> | ||||||
|     <div> |     <div> | ||||||
|         <a href="https://coreui.io" target="_blank" rel="noopener noreferrer">CoreUI</a> |       <a href="https://coreui.io" target="_blank" rel="noopener noreferrer"> | ||||||
|  |         CoreUI | ||||||
|  |       </a> | ||||||
|       <span className="ml-1">© 2020 creativeLabs.</span> |       <span className="ml-1">© 2020 creativeLabs.</span> | ||||||
|     </div> |     </div> | ||||||
|     <div className="mfs-auto"> |     <div className="mfs-auto"> | ||||||
|       <span className="mr-1">Powered by</span> |       <span className="mr-1">Powered by</span> | ||||||
|         <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">CoreUI for React</a> |       <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> | ||||||
|  |         CoreUI for React | ||||||
|  |       </a> | ||||||
|     </div> |     </div> | ||||||
|   </CFooter> |   </CFooter> | ||||||
|   ) | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default React.memo(TheFooter) | export default React.memo(TheFooter); | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { useSelector, useDispatch } from 'react-redux' | import { useSelector, useDispatch } from 'react-redux'; | ||||||
| import { logout } from '../utils/authHelper' |  | ||||||
| import { | import { | ||||||
|   CHeader, |   CHeader, | ||||||
|   CToggler, |   CToggler, | ||||||
| @@ -8,71 +7,65 @@ import { | |||||||
|   CHeaderNav, |   CHeaderNav, | ||||||
|   CSubheader, |   CSubheader, | ||||||
|   CBreadcrumbRouter, |   CBreadcrumbRouter, | ||||||
|   CLink |   CLink, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react' | import CIcon from '@coreui/icons-react'; | ||||||
| import { cilAccountLogout } from '@coreui/icons'; | import { cilAccountLogout } from '@coreui/icons'; | ||||||
| import routes from '../routes' | import { logout } from '../utils/authHelper'; | ||||||
|  | import routes from '../routes'; | ||||||
|  |  | ||||||
| const TheHeader = () => { | const TheHeader = () => { | ||||||
|   const dispatch = useDispatch() |   const dispatch = useDispatch(); | ||||||
|   const sidebarShow = useSelector(state => state.sidebarShow) |   const sidebarShow = useSelector((state) => state.sidebarShow); | ||||||
|  |  | ||||||
|   const toggleSidebar = () => { |   const toggleSidebar = () => { | ||||||
|     const val = [true, 'responsive'].includes(sidebarShow) ? false : 'responsive' |     const val = [true, 'responsive'].includes(sidebarShow) ? false : 'responsive'; | ||||||
|     dispatch({type: 'set', sidebarShow: val}) |     dispatch({ type: 'set', sidebarShow: val }); | ||||||
|   } |   }; | ||||||
|  |  | ||||||
|   const toggleSidebarMobile = () => { |   const toggleSidebarMobile = () => { | ||||||
|     const val = [false, 'responsive'].includes(sidebarShow) ? true : 'responsive' |     const val = [false, 'responsive'].includes(sidebarShow) ? true : 'responsive'; | ||||||
|     dispatch({type: 'set', sidebarShow: val}) |     dispatch({ type: 'set', sidebarShow: val }); | ||||||
|   } |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CHeader withSubheader> |     <CHeader withSubheader> | ||||||
|     <CToggler |       <CToggler inHeader className="ml-md-3 d-lg-none" onClick={toggleSidebarMobile} /> | ||||||
|       inHeader |       <CToggler inHeader className="ml-3 d-md-down-none" onClick={toggleSidebar} /> | ||||||
|       className="ml-md-3 d-lg-none" |  | ||||||
|       onClick={toggleSidebarMobile} |  | ||||||
|     /> |  | ||||||
|     <CToggler |  | ||||||
|       inHeader |  | ||||||
|       className="ml-3 d-md-down-none" |  | ||||||
|       onClick={toggleSidebar} |  | ||||||
|     /> |  | ||||||
|       <CHeaderBrand className="mx-auto d-lg-none" to="/"> |       <CHeaderBrand className="mx-auto d-lg-none" to="/"> | ||||||
|         <CIcon name="logo" height="48" alt="Logo" /> |         <CIcon name="logo" height="48" alt="Logo" /> | ||||||
|       </CHeaderBrand> |       </CHeaderBrand> | ||||||
|  |  | ||||||
|       <CHeaderNav className="d-md-down-none mr-auto"> |       <CHeaderNav className="d-md-down-none mr-auto" /> | ||||||
|       </CHeaderNav> |  | ||||||
|  |  | ||||||
|       <CHeaderNav className="px-3"> |       <CHeaderNav className="px-3"> | ||||||
|         <CLink className="c-subheader-nav-link"> |         <CLink className="c-subheader-nav-link"> | ||||||
|         <CIcon name="cilAccountLogout" content={cilAccountLogout} size="2xl" onClick={() => { logout();}} /> |           <CIcon | ||||||
|  |             name="cilAccountLogout" | ||||||
|  |             content={cilAccountLogout} | ||||||
|  |             size="2xl" | ||||||
|  |             onClick={() => { | ||||||
|  |               logout(); | ||||||
|  |             }} | ||||||
|  |           /> | ||||||
|         </CLink> |         </CLink> | ||||||
|       </CHeaderNav> |       </CHeaderNav> | ||||||
|  |  | ||||||
|       <CSubheader className="px-3 justify-content-between"> |       <CSubheader className="px-3 justify-content-between"> | ||||||
|         <CBreadcrumbRouter  |         <CBreadcrumbRouter className="border-0 c-subheader-nav m-0 px-0 px-md-3" routes={routes} /> | ||||||
|           className="border-0 c-subheader-nav m-0 px-0 px-md-3"  |  | ||||||
|           routes={routes} |  | ||||||
|         /> |  | ||||||
|         <div className="d-md-down-none mfe-2 c-subheader-nav"> |         <div className="d-md-down-none mfe-2 c-subheader-nav"> | ||||||
|             <CLink  |           <CLink className="c-subheader-nav-link" aria-current="page" to="/devices"> | ||||||
|               className="c-subheader-nav-link"  |             <CIcon name="cil-graph" alt="Dashboard" /> | ||||||
|               aria-current="page"  |              Dashboard | ||||||
|               to="/devices" |  | ||||||
|             > |  | ||||||
|               <CIcon name="cil-graph" alt="Dashboard" /> Dashboard |  | ||||||
|           </CLink> |           </CLink> | ||||||
|           <CLink className="c-subheader-nav-link" href="#"> |           <CLink className="c-subheader-nav-link" href="#"> | ||||||
|               <CIcon name="cil-settings" alt="Settings" /> Settings |             <CIcon name="cil-settings" alt="Settings" /> | ||||||
|  |              Settings | ||||||
|           </CLink> |           </CLink> | ||||||
|         </div> |         </div> | ||||||
|       </CSubheader> |       </CSubheader> | ||||||
|     </CHeader> |     </CHeader> | ||||||
|   ) |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default TheHeader | export default TheHeader; | ||||||
|   | |||||||
| @@ -1,20 +1,14 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { |  | ||||||
|   TheContent, |  | ||||||
|   TheSidebar, |  | ||||||
|   TheFooter, |  | ||||||
|   TheHeader |  | ||||||
| } from './index'; |  | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
|  | import TheContent from './TheContent'; | ||||||
|  | import TheSidebar from './TheSidebar'; | ||||||
|  | import TheFooter from './TheFooter'; | ||||||
|  | import TheHeader from './TheHeader'; | ||||||
|  |  | ||||||
| const TheLayout = (props) => { | const TheLayout = (props) => { | ||||||
|   const { isLoggedIn } = useSelector(state => state.connected); |   const { isLoggedIn } = useSelector((state) => state.connected); | ||||||
|   if (isLoggedIn) { |   if (isLoggedIn) { | ||||||
|     return ( |     return <div>{props.children}</div>; | ||||||
|         <div> |  | ||||||
|             {props.children} |  | ||||||
|         </div> |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
|   return ( |   return ( | ||||||
|     <div className="c-app c-default-layout"> |     <div className="c-app c-default-layout"> | ||||||
| @@ -27,7 +21,7 @@ const TheLayout = (props) => { | |||||||
|         <TheFooter /> |         <TheFooter /> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ) |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default TheLayout | export default TheLayout; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { useSelector, useDispatch } from 'react-redux' | import { useSelector, useDispatch } from 'react-redux'; | ||||||
| import { | import { | ||||||
|   CCreateElement, |   CCreateElement, | ||||||
|   CSidebar, |   CSidebar, | ||||||
| @@ -10,49 +10,37 @@ import { | |||||||
|   CSidebarMinimizer, |   CSidebarMinimizer, | ||||||
|   CSidebarNavDropdown, |   CSidebarNavDropdown, | ||||||
|   CSidebarNavItem, |   CSidebarNavItem, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
|  |  | ||||||
| import CIcon from '@coreui/icons-react' | import CIcon from '@coreui/icons-react'; | ||||||
|  |  | ||||||
| // sidebar nav config | // sidebar nav config | ||||||
| import navigation from './_nav' | import navigation from './_nav'; | ||||||
|  |  | ||||||
| const TheSidebar = () => { | const TheSidebar = () => { | ||||||
|   const dispatch = useDispatch() |   const dispatch = useDispatch(); | ||||||
|   const show = useSelector(state => state.sidebarShow) |   const show = useSelector((state) => state.sidebarShow); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <CSidebar |     <CSidebar show={show} onShowChange={(val) => dispatch({ type: 'set', sidebarShow: val })}> | ||||||
|       show={show} |  | ||||||
|       onShowChange={(val) => dispatch({type: 'set', sidebarShow: val })} |  | ||||||
|     > |  | ||||||
|       <CSidebarBrand className="d-md-down-none" to="/devices"> |       <CSidebarBrand className="d-md-down-none" to="/devices"> | ||||||
|         <CIcon |         <CIcon className="c-sidebar-brand-full" name="logo-negative" height={35} /> | ||||||
|           className="c-sidebar-brand-full" |         <CIcon className="c-sidebar-brand-minimized" name="sygnet" height={35} /> | ||||||
|           name="logo-negative" |  | ||||||
|           height={35} |  | ||||||
|         /> |  | ||||||
|         <CIcon |  | ||||||
|           className="c-sidebar-brand-minimized" |  | ||||||
|           name="sygnet" |  | ||||||
|           height={35} |  | ||||||
|         /> |  | ||||||
|       </CSidebarBrand> |       </CSidebarBrand> | ||||||
|       <CSidebarNav> |       <CSidebarNav> | ||||||
|  |  | ||||||
|         <CCreateElement |         <CCreateElement | ||||||
|           items={navigation} |           items={navigation} | ||||||
|           components={{ |           components={{ | ||||||
|             CSidebarNavDivider, |             CSidebarNavDivider, | ||||||
|             CSidebarNavDropdown, |             CSidebarNavDropdown, | ||||||
|             CSidebarNavItem, |             CSidebarNavItem, | ||||||
|             CSidebarNavTitle |             CSidebarNavTitle, | ||||||
|           }} |           }} | ||||||
|         /> |         /> | ||||||
|       </CSidebarNav> |       </CSidebarNav> | ||||||
|       <CSidebarMinimizer className="c-d-md-down-none" /> |       <CSidebarMinimizer className="c-d-md-down-none" /> | ||||||
|     </CSidebar> |     </CSidebar> | ||||||
|   ) |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default React.memo(TheSidebar) | export default React.memo(TheSidebar); | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| const _nav =  [ | const nav = [ | ||||||
|   { |   { | ||||||
|     _tag: 'CSidebarNavItem', |     _tag: 'CSidebarNavItem', | ||||||
|     name: 'List of Devices', |     name: 'List of Devices', | ||||||
|     to: '/devices', |     to: '/devices', | ||||||
|     icon: 'cilNotes' |     icon: 'cilNotes', | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     _tag: 'CSidebarNavItem', |     _tag: 'CSidebarNavItem', | ||||||
| @@ -17,6 +17,6 @@ const _nav =  [ | |||||||
|     to: '/Settings', |     to: '/Settings', | ||||||
|     icon: 'cil-settings', |     icon: 'cil-settings', | ||||||
|   }, |   }, | ||||||
| ] | ]; | ||||||
|  |  | ||||||
| export default _nav | export default nav; | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| import TheContent from './TheContent' |  | ||||||
| import TheFooter from './TheFooter' |  | ||||||
| import TheHeader from './TheHeader' |  | ||||||
| import TheLayout from './TheLayout' |  | ||||||
| import TheSidebar from './TheSidebar' |  | ||||||
|  |  | ||||||
| export { |  | ||||||
|   TheContent, |  | ||||||
|   TheFooter, |  | ||||||
|   TheHeader, |  | ||||||
|   TheLayout, |  | ||||||
|   TheSidebar |  | ||||||
| } |  | ||||||
| @@ -1,13 +1,11 @@ | |||||||
| body { | body { | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', |   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', | ||||||
|     'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', |     'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; | ||||||
|     sans-serif; |  | ||||||
|   -webkit-font-smoothing: antialiased; |   -webkit-font-smoothing: antialiased; | ||||||
|   -moz-osx-font-smoothing: grayscale; |   -moz-osx-font-smoothing: grayscale; | ||||||
| } | } | ||||||
|  |  | ||||||
| code { | code { | ||||||
|   font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', |   font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; | ||||||
|     monospace; |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/index.js
									
									
									
									
									
								
							| @@ -1,11 +1,11 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import ReactDOM from 'react-dom'; | import ReactDOM from 'react-dom'; | ||||||
| import './index.css'; | import './index.css'; | ||||||
| import App from './App.js'; |  | ||||||
| import store from './store'; |  | ||||||
| import { Provider } from 'react-redux'; | import { Provider } from 'react-redux'; | ||||||
| import 'bootstrap/dist/css/bootstrap.min.css' | import App from './App'; | ||||||
| import { icons } from './assets/icons' | import store from './store'; | ||||||
|  | import 'bootstrap/dist/css/bootstrap.min.css'; | ||||||
|  | import { icons } from './assets/icons'; | ||||||
|  |  | ||||||
| React.icons = icons; | React.icons = icons; | ||||||
|  |  | ||||||
| @@ -15,5 +15,5 @@ ReactDOM.render( | |||||||
|       <App /> |       <App /> | ||||||
|     </Provider> |     </Provider> | ||||||
|   </React.StrictMode>, |   </React.StrictMode>, | ||||||
|   document.getElementById('root') |   document.getElementById('root'), | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| // If you want to override variables do it here | // If you want to override variables do it here | ||||||
| @import "variables"; | @import 'variables'; | ||||||
|  |  | ||||||
| // Import CoreUI styles | // Import CoreUI styles | ||||||
| @import "~@coreui/coreui/scss/coreui.scss"; | @import '~@coreui/coreui/scss/coreui.scss'; | ||||||
|  |  | ||||||
| // Some temp fixes | // Some temp fixes | ||||||
| @import "fixes"; | @import 'fixes'; | ||||||
|  |  | ||||||
| // If you want to add something do it here | // If you want to add something do it here | ||||||
| @import "custom"; | @import 'custom'; | ||||||
|   | |||||||
| @@ -15,9 +15,7 @@ const isLocalhost = Boolean( | |||||||
|     // [::1] is the IPv6 localhost address. |     // [::1] is the IPv6 localhost address. | ||||||
|     window.location.hostname === '[::1]' || |     window.location.hostname === '[::1]' || | ||||||
|     // 127.0.0.0/8 are considered localhost for IPv4. |     // 127.0.0.0/8 are considered localhost for IPv4. | ||||||
|     window.location.hostname.match( |     window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/), | ||||||
|       /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ |  | ||||||
|     ) |  | ||||||
| ); | ); | ||||||
|  |  | ||||||
| export function register(config) { | export function register(config) { | ||||||
| @@ -43,7 +41,7 @@ export function register(config) { | |||||||
|         navigator.serviceWorker.ready.then(() => { |         navigator.serviceWorker.ready.then(() => { | ||||||
|           console.log( |           console.log( | ||||||
|             'This web app is being served cache-first by a service ' + |             'This web app is being served cache-first by a service ' + | ||||||
|               'worker. To learn more, visit https://bit.ly/CRA-PWA' |               'worker. To learn more, visit https://bit.ly/CRA-PWA', | ||||||
|           ); |           ); | ||||||
|         }); |         }); | ||||||
|       } else { |       } else { | ||||||
| @@ -71,7 +69,7 @@ function registerValidSW(swUrl, config) { | |||||||
|               // content until all client tabs are closed. |               // content until all client tabs are closed. | ||||||
|               console.log( |               console.log( | ||||||
|                 'New content is available and will be used when all ' + |                 'New content is available and will be used when all ' + | ||||||
|                   'tabs for this page are closed. See https://bit.ly/CRA-PWA.' |                   'tabs for this page are closed. See https://bit.ly/CRA-PWA.', | ||||||
|               ); |               ); | ||||||
|  |  | ||||||
|               // Execute callback |               // Execute callback | ||||||
| @@ -122,9 +120,7 @@ function checkValidServiceWorker(swUrl, config) { | |||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|     .catch(() => { |     .catch(() => { | ||||||
|       console.log( |       console.log('No internet connection found. App is running in offline mode.'); | ||||||
|         'No internet connection found. App is running in offline mode.' |  | ||||||
|       ); |  | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								src/store.js
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/store.js
									
									
									
									
									
								
							| @@ -1,20 +1,20 @@ | |||||||
| import { createStore } from 'redux' | import { createStore } from 'redux'; | ||||||
|  |  | ||||||
| const initialState = { | const initialState = { | ||||||
|   sidebarShow: 'responsive', |   sidebarShow: 'responsive', | ||||||
|   connected: false, |   connected: false, | ||||||
|   selectedDeviceId: null, |   selectedDeviceId: null, | ||||||
|   selectedDevice: null |   selectedDevice: null, | ||||||
| } | }; | ||||||
|  |  | ||||||
| const changeState = (state = initialState, { type, ...rest }) => { | const changeState = (state = initialState, { type, ...rest }) => { | ||||||
|   switch (type) { |   switch (type) { | ||||||
|     case 'set': |     case 'set': | ||||||
|       return {...state, ...rest } |       return { ...state, ...rest }; | ||||||
|     default: |     default: | ||||||
|       return state |       return state; | ||||||
|   } |  | ||||||
|   } |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
| const store = createStore(changeState) | const store = createStore(changeState); | ||||||
| export default store | export default store; | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| export const logout = () => { | export const logout = () => { | ||||||
|   sessionStorage.clear(); |   sessionStorage.clear(); | ||||||
|   window.location.replace('/'); |   window.location.replace('/'); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export const getToken = () => { | export const getToken = () => { | ||||||
|   const token = sessionStorage.getItem('access_token'); |   const token = sessionStorage.getItem('access_token'); | ||||||
|   if (token === undefined || token === null) { |   if (token === undefined || token === null) { | ||||||
|     logout(); |     logout(); | ||||||
|         return; |     return null; | ||||||
|   } |   } | ||||||
|   return token; |   return token; | ||||||
| } | }; | ||||||
|   | |||||||
| @@ -11,37 +11,38 @@ axiosRetry(axiosInstance , { | |||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| axiosInstance.defaults.headers.get['Accept'] = 'application/json'   // default header for all get request | axiosInstance.defaults.headers.get.Accept = 'application/json'; | ||||||
| axiosInstance.defaults.headers.post['Accept'] = 'application/json'  // default header for all POST request | axiosInstance.defaults.headers.post.Accept = 'application/json'; | ||||||
|  |  | ||||||
| axiosInstance.interceptors.request.use(function(config) { | axiosInstance.interceptors.request.use((config) => { | ||||||
|  |   const newConfig = config; | ||||||
|   const url = sessionStorage.getItem('gw_url'); |   const url = sessionStorage.getItem('gw_url'); | ||||||
|     if(url !== undefined && url !== null && !config.url.includes(url)){ |   if (url !== undefined && url !== null && !newConfig.url.includes(url)) { | ||||||
|         config.url = url + config.url; |     newConfig.url = url + newConfig.url; | ||||||
|   } |   } | ||||||
|     return config; |   return newConfig; | ||||||
| }); | }); | ||||||
|  |  | ||||||
| axiosInstance.interceptors.response.use( | axiosInstance.interceptors.response.use( | ||||||
|   // Success actions |   // Success actions | ||||||
|   undefined, |   undefined, | ||||||
|     function(error) { |   (error) => { | ||||||
|     console.log(error); |     console.log(error); | ||||||
|     switch (error.response.status) { |     switch (error.response.status) { | ||||||
|       case 401: |       case 401: | ||||||
|                 console.log('Error 401 ' + error ); |         console.log(`Error 401 ${error}`); | ||||||
|         break; |         break; | ||||||
|       case 403: |       case 403: | ||||||
|                 console.log('Error 403 ' + error ); |         console.log(`Error 403 ${error}`); | ||||||
|         sessionStorage.clear(); |         sessionStorage.clear(); | ||||||
|         window.location.href = '/'; |         window.location.href = '/'; | ||||||
|         break; |         break; | ||||||
|       default: |       default: | ||||||
|                 console.log('Default ' + error.response.status); |         console.log(`Default ${error.response.status}`); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     return Promise.reject(error); |     return Promise.reject(error); | ||||||
|     } |   }, | ||||||
| ); | ); | ||||||
|  |  | ||||||
| export default axiosInstance; | export default axiosInstance; | ||||||
| @@ -1,12 +1,12 @@ | |||||||
| export const cleanTimestamp = (timestamp) => { | export const cleanTimestamp = (timestamp) => timestamp.replace('T', ' ').replace('Z', ' '); | ||||||
|     return timestamp.replace('T', ' ').replace('Z', ' '); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const cropStringWithEllipsis = (text, lengthWithEllipsis) => { | export const cropStringWithEllipsis = (text, lengthWithEllipsis) => { | ||||||
|   if (!text || text.length === '') return 'N/A'; |   if (!text || text.length === '') return 'N/A'; | ||||||
|  |  | ||||||
|     return text.length > lengthWithEllipsis ? text.substring(0, (lengthWithEllipsis - 3)) + '\u2026' : text; |   return text.length > lengthWithEllipsis | ||||||
| } |     ? `${text.substring(0, lengthWithEllipsis - 3)}\u2026` | ||||||
|  |     : text; | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const cleanBytesString = (bytes, decimals = 2) => { | export const cleanBytesString = (bytes, decimals = 2) => { | ||||||
|   const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; |   const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; | ||||||
| @@ -15,17 +15,17 @@ export const cleanBytesString = (bytes, decimals = 2) => { | |||||||
|   } |   } | ||||||
|   const k = 1024; |   const k = 1024; | ||||||
|   const dm = decimals < 0 ? 0 : decimals; |   const dm = decimals < 0 ? 0 : decimals; | ||||||
|     const i = parseInt(Math.floor(Math.log(bytes) / Math.log(k))); |   const i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10); | ||||||
|     return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; |   return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; | ||||||
| } | }; | ||||||
|  |  | ||||||
| export const convertDateToUtc = (date) => { | export const convertDateToUtc = (date) => { | ||||||
|   const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000); |   const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000); | ||||||
|   return utcDate; |   return utcDate; | ||||||
| } | }; | ||||||
|  |  | ||||||
| export const convertDateFromUtc = (utcDate) => { | export const convertDateFromUtc = (utcDate) => { | ||||||
|   const dateObj = new Date(); |   const dateObj = new Date(); | ||||||
|   const date = new Date(utcDate.getTime() - dateObj.getTimezoneOffset() * 60000); |   const date = new Date(utcDate.getTime() - dateObj.getTimezoneOffset() * 60000); | ||||||
|   return date; |   return date; | ||||||
| } | }; | ||||||
|   | |||||||
| @@ -1,36 +1,29 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react' | import { CCard, CCardBody, CCardHeader, CCol, CRow } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react' | import CIcon from '@coreui/icons-react'; | ||||||
| import { brandSet } from '@coreui/icons' | import { brandSet } from '@coreui/icons'; | ||||||
| import { DocsLink } from 'src/reusable' | import { DocsLink } from 'src/reusable'; | ||||||
|  |  | ||||||
| const toKebabCase = (str) => { | const toKebabCase = (str) => str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase(); | ||||||
|   return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const getIconsView = iconset => { | export const getIconsView = (iconset) => | ||||||
|   return Object.entries(iconset).map(([name, value]) => ( |   Object.entries(iconset).map(([name, value]) => ( | ||||||
|     <CCol className="mb-5" xs="6" sm="4" md="3" xl="2" key={name}> |     <CCol className="mb-5" xs="6" sm="4" md="3" xl="2" key={name}> | ||||||
|       <CIcon content={value} size="2xl" /> |       <CIcon content={value} size="2xl" /> | ||||||
|       <div>{toKebabCase(name)}</div> |       <div>{toKebabCase(name)}</div> | ||||||
|     </CCol> |     </CCol> | ||||||
|   )) |   )); | ||||||
| } |  | ||||||
|  |  | ||||||
| const CoreUIIcons = () => { | const CoreUIIcons = () => ( | ||||||
|   return ( |  | ||||||
|   <CCard> |   <CCard> | ||||||
|     <CCardHeader> |     <CCardHeader> | ||||||
|       Brand Icons |       Brand Icons | ||||||
|       <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> |       <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> | ||||||
|     </CCardHeader> |     </CCardHeader> | ||||||
|     <CCardBody> |     <CCardBody> | ||||||
|         <CRow className="text-center"> |       <CRow className="text-center">{getIconsView(brandSet)}</CRow> | ||||||
|           {getIconsView(brandSet)} |  | ||||||
|         </CRow> |  | ||||||
|     </CCardBody> |     </CCardBody> | ||||||
|   </CCard> |   </CCard> | ||||||
|   ) | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default CoreUIIcons | export default CoreUIIcons; | ||||||
|   | |||||||
| @@ -1,23 +1,18 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react' | import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react'; | ||||||
| import { freeSet } from '@coreui/icons' | import { freeSet } from '@coreui/icons'; | ||||||
| import { getIconsView } from '../brands/Brands.js' | import { DocsLink } from 'src/reusable'; | ||||||
| import { DocsLink } from 'src/reusable' | import { getIconsView } from '../brands/Brands.js'; | ||||||
|  |  | ||||||
| const CoreUIIcons = () => { | const CoreUIIcons = () => ( | ||||||
|   return ( |  | ||||||
|   <CCard> |   <CCard> | ||||||
|     <CCardHeader> |     <CCardHeader> | ||||||
|         Free Icons / as CIcon{' '} |       Free Icons / as CIcon <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> | ||||||
|         <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub"/> |  | ||||||
|     </CCardHeader> |     </CCardHeader> | ||||||
|     <CCardBody> |     <CCardBody> | ||||||
|         <CRow className="text-center"> |       <CRow className="text-center">{getIconsView(freeSet)}</CRow> | ||||||
|           {getIconsView(freeSet)} |  | ||||||
|         </CRow> |  | ||||||
|     </CCardBody> |     </CCardBody> | ||||||
|   </CCard> |   </CCard> | ||||||
|   ) | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default CoreUIIcons | export default CoreUIIcons; | ||||||
|   | |||||||
| @@ -1,23 +1,19 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react' | import { CCard, CCardBody, CCardHeader, CRow } from '@coreui/react'; | ||||||
| import { getIconsView } from '../brands/Brands.js' | import { flagSet } from '@coreui/icons'; | ||||||
| import { flagSet } from '@coreui/icons' | import { DocsLink } from 'src/reusable'; | ||||||
| import { DocsLink } from 'src/reusable' | import { getIconsView } from '../brands/Brands.js'; | ||||||
|  |  | ||||||
| const CoreUIIcons = () => { | const CoreUIIcons = () => ( | ||||||
|   return ( |  | ||||||
|   <CCard> |   <CCard> | ||||||
|     <CCardHeader> |     <CCardHeader> | ||||||
|       Flag Icons |       Flag Icons | ||||||
|       <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> |       <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> | ||||||
|     </CCardHeader> |     </CCardHeader> | ||||||
|     <CCardBody> |     <CCardBody> | ||||||
|         <CRow className="text-center"> |       <CRow className="text-center">{getIconsView(flagSet)}</CRow> | ||||||
|           {getIconsView(flagSet)} |  | ||||||
|         </CRow> |  | ||||||
|     </CCardBody> |     </CCardBody> | ||||||
|   </CCard> |   </CCard> | ||||||
|   ) | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default CoreUIIcons | export default CoreUIIcons; | ||||||
|   | |||||||
| @@ -2,6 +2,4 @@ import CoreUIIcons from './coreui-icons'; | |||||||
| import Flags from './flags'; | import Flags from './flags'; | ||||||
| import Brands from './brands'; | import Brands from './brands'; | ||||||
|  |  | ||||||
| export { | export { CoreUIIcons, Flags, Brands }; | ||||||
|   CoreUIIcons, Flags, Brands |  | ||||||
| }; |  | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import DeviceList from '../../components/DeviceList'; | import DeviceList from '../../components/DeviceList'; | ||||||
|  |  | ||||||
| const DeviceListPage = (props) => { | const DeviceListPage = () => ( | ||||||
|     return ( |  | ||||||
|   <div className="App"> |   <div className="App"> | ||||||
|     <DeviceList /> |     <DeviceList /> | ||||||
|   </div> |   </div> | ||||||
| ); | ); | ||||||
| } |  | ||||||
|  |  | ||||||
| export default DeviceListPage; | export default DeviceListPage; | ||||||
| @@ -1,20 +1,17 @@ | |||||||
| import React, { useEffect } from 'react' | import React, { useEffect } from 'react'; | ||||||
| import { useDispatch, useSelector } from 'react-redux'; | import { useDispatch, useSelector } from 'react-redux'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
| import { | import { CRow, CCol } from '@coreui/react'; | ||||||
|     CRow, |  | ||||||
|     CCol |  | ||||||
|   } from '@coreui/react' |  | ||||||
| import DeviceHealth from '../../components/DeviceHealth'; | import DeviceHealth from '../../components/DeviceHealth'; | ||||||
| import DeviceConfiguration from '../../components/DeviceConfiguration'; | import DeviceConfiguration from '../../components/DeviceConfiguration'; | ||||||
| import DeviceActions from '../../components/DeviceActions' | import DeviceActions from '../../components/DeviceActions'; | ||||||
|  |  | ||||||
| const DevicePage = (props) => { | const DevicePage = () => { | ||||||
|   const dispatch = useDispatch(); |   const dispatch = useDispatch(); | ||||||
|  |  | ||||||
|   // Storing the deviceId in the store |   // Storing the deviceId in the store | ||||||
|     let selectedDeviceId = useSelector(state => state.selectedDeviceId); |   let selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|     let { deviceId } = useParams(); |   const { deviceId } = useParams(); | ||||||
|  |  | ||||||
|   if (!selectedDeviceId || selectedDeviceId !== deviceId) { |   if (!selectedDeviceId || selectedDeviceId !== deviceId) { | ||||||
|     dispatch({ type: 'set', selectedDeviceId: deviceId }); |     dispatch({ type: 'set', selectedDeviceId: deviceId }); | ||||||
| @@ -29,17 +26,17 @@ const DevicePage = (props) => { | |||||||
|     <> |     <> | ||||||
|       <div className="App"> |       <div className="App"> | ||||||
|         <CRow> |         <CRow> | ||||||
|                 <CCol xs='12' sm='6'> |           <CCol xs="12" sm="6"> | ||||||
|             <DeviceConfiguration /> |             <DeviceConfiguration /> | ||||||
|           </CCol> |           </CCol> | ||||||
|                 <CCol xs='12' sm='6'> |           <CCol xs="12" sm="6"> | ||||||
|             <DeviceHealth /> |             <DeviceHealth /> | ||||||
|             <DeviceActions /> |             <DeviceActions /> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|       </div> |       </div> | ||||||
|     </> |     </> | ||||||
|       ) |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default DevicePage; | export default DevicePage; | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| import React, { useState, useEffect } from 'react' | import React, { useState, useEffect } from 'react'; | ||||||
| import { | import { | ||||||
|   CButton, |   CButton, | ||||||
|   CCard, |   CCard, | ||||||
| @@ -14,46 +14,28 @@ import { | |||||||
|   CRow, |   CRow, | ||||||
|   CPopover, |   CPopover, | ||||||
|   CAlert, |   CAlert, | ||||||
|   CInvalidFeedback |   CInvalidFeedback, | ||||||
| } from '@coreui/react' | } from '@coreui/react'; | ||||||
| import CIcon from '@coreui/icons-react' | import CIcon from '@coreui/icons-react'; | ||||||
| import { cilUser, cilLockLocked, cilLink } from '@coreui/icons'; | import { cilUser, cilLockLocked, cilLink } from '@coreui/icons'; | ||||||
| import { useDispatch } from 'react-redux'; | import { useDispatch } from 'react-redux'; | ||||||
| import axiosInstance from '../../utils/axiosInstance'; | import axiosInstance from '../../utils/axiosInstance'; | ||||||
|  |  | ||||||
|  |  | ||||||
| const Login = () => { | const Login = () => { | ||||||
|   const dispatch = useDispatch(); |   const dispatch = useDispatch(); | ||||||
|   const [userId, setUsername] = useState(''); |   const [userId, setUsername] = useState(''); | ||||||
|   const [password, setPassword] = useState(''); |   const [password, setPassword] = useState(''); | ||||||
|   const [gatewayUrl, setGatewayUrl] = useState('https://ucentral.dpaas.arilia.com:16001/api/v1'); |   const [gatewayUrl, setGatewayUrl] = useState('https://ucentral.dpaas.arilia.com:16001/api/v1'); | ||||||
| 	const [error, setError] = useState(false); |   const [hadError, setHadError] = useState(false); | ||||||
|   const [emptyUsername, setEmptyUsername] = useState(false); |   const [emptyUsername, setEmptyUsername] = useState(false); | ||||||
|   const [emptyPassword, setEmptyPassword] = useState(false); |   const [emptyPassword, setEmptyPassword] = useState(false); | ||||||
|   const [emptyGateway, setEmptyGateway] = useState(false); |   const [emptyGateway, setEmptyGateway] = useState(false); | ||||||
|    |   const placeholderUrl = 'Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001/api/v1)'; | ||||||
| 	const onKeyDown = (event) => { |   const loginErrorText = | ||||||
| 	if(event.code === 'Enter' && formValidation()){ |     'Login error, confirm that your username, password and gateway url are valid'; | ||||||
| 		SignIn({ userId, password }); |  | ||||||
| 	} |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	const SignIn = (credentials) => { |  | ||||||
| 		axiosInstance.post(`${gatewayUrl}/oauth2`, credentials) |  | ||||||
| 		.then((response) => { |  | ||||||
| 			sessionStorage.setItem('gw_url', gatewayUrl); |  | ||||||
| 			sessionStorage.setItem('access_token', response.data.access_token); |  | ||||||
| 			dispatch({type: 'set', connected: true}); |  | ||||||
| 		}) |  | ||||||
| 		.catch(error => { |  | ||||||
| 			console.log(error); |  | ||||||
| 			setError(true); |  | ||||||
| 			console.log(error.response); |  | ||||||
| 		}); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
|   const formValidation = () => { |   const formValidation = () => { | ||||||
| 		setError(false); |     setHadError(false); | ||||||
|  |  | ||||||
|     let isSuccesful = true; |     let isSuccesful = true; | ||||||
|  |  | ||||||
| @@ -75,9 +57,36 @@ const Login = () => { | |||||||
|     return isSuccesful; |     return isSuccesful; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| 	useEffect(() => { if (emptyUsername) setEmptyUsername(false); }, [userId]); |   const SignIn = (credentials) => { | ||||||
| 	useEffect(() => { if (emptyPassword) setEmptyPassword(false); }, [password]); |     axiosInstance | ||||||
| 	useEffect(() => { if (emptyGateway) setEmptyGateway(false); }, [gatewayUrl]); |       .post(`${gatewayUrl}/oauth2`, credentials) | ||||||
|  |       .then((response) => { | ||||||
|  |         sessionStorage.setItem('gw_url', gatewayUrl); | ||||||
|  |         sessionStorage.setItem('access_token', response.data.access_token); | ||||||
|  |         dispatch({ type: 'set', connected: true }); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         console.log(error); | ||||||
|  |         setHadError(true); | ||||||
|  |         console.log(error.response); | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const onKeyDown = (event) => { | ||||||
|  |     if (event.code === 'Enter' && formValidation()) { | ||||||
|  |       SignIn({ userId, password }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (emptyUsername) setEmptyUsername(false); | ||||||
|  |   }, [userId]); | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (emptyPassword) setEmptyPassword(false); | ||||||
|  |   }, [password]); | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (emptyGateway) setEmptyGateway(false); | ||||||
|  |   }, [gatewayUrl]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="c-app c-default-layout flex-row align-items-center"> |     <div className="c-app c-default-layout flex-row align-items-center"> | ||||||
| @@ -98,7 +107,15 @@ const Login = () => { | |||||||
|                           </CInputGroupText> |                           </CInputGroupText> | ||||||
|                         </CInputGroupPrepend> |                         </CInputGroupPrepend> | ||||||
|                       </CPopover> |                       </CPopover> | ||||||
| 										<CInput invalid = { emptyUsername } autoFocus required type="text" placeholder="Username" autoComplete="username" onChange={event => setUsername(event.target.value)}/> |                       <CInput | ||||||
|  |                         invalid={emptyUsername} | ||||||
|  |                         autoFocus | ||||||
|  |                         required | ||||||
|  |                         type="text" | ||||||
|  |                         placeholder="Username" | ||||||
|  |                         autoComplete="username" | ||||||
|  |                         onChange={(event) => setUsername(event.target.value)} | ||||||
|  |                       /> | ||||||
|                       <CInvalidFeedback className="help-block"> |                       <CInvalidFeedback className="help-block"> | ||||||
|                         Please enter your username |                         Please enter your username | ||||||
|                       </CInvalidFeedback> |                       </CInvalidFeedback> | ||||||
| @@ -107,11 +124,18 @@ const Login = () => { | |||||||
|                       <CPopover content="Password"> |                       <CPopover content="Password"> | ||||||
|                         <CInputGroupPrepend> |                         <CInputGroupPrepend> | ||||||
|                           <CInputGroupText> |                           <CInputGroupText> | ||||||
| 												<CIcon name="cilLockLocked" content={ cilLockLocked }/> |                             <CIcon content={cilLockLocked} /> | ||||||
|                           </CInputGroupText> |                           </CInputGroupText> | ||||||
|                         </CInputGroupPrepend> |                         </CInputGroupPrepend> | ||||||
|                       </CPopover> |                       </CPopover> | ||||||
| 										<CInput invalid = { emptyPassword } type="password" required placeholder="Password" autoComplete="current-password" onChange={event => setPassword(event.target.value)} /> |                       <CInput | ||||||
|  |                         invalid={emptyPassword} | ||||||
|  |                         required | ||||||
|  |                         type="password" | ||||||
|  |                         placeholder="Password" | ||||||
|  |                         autoComplete="current-password" | ||||||
|  |                         onChange={(event) => setPassword(event.target.value)} | ||||||
|  |                       /> | ||||||
|                       <CInvalidFeedback className="help-block"> |                       <CInvalidFeedback className="help-block"> | ||||||
|                         Please enter your password |                         Please enter your password | ||||||
|                       </CInvalidFeedback> |                       </CInvalidFeedback> | ||||||
| @@ -124,25 +148,40 @@ const Login = () => { | |||||||
|                           </CInputGroupText> |                           </CInputGroupText> | ||||||
|                         </CInputGroupPrepend> |                         </CInputGroupPrepend> | ||||||
|                       </CPopover> |                       </CPopover> | ||||||
| 										<CInput invalid = { emptyGateway } type="text" required placeholder="Gateway URL (ex: https://ucentral.dpaas.arilia.com:16001/api/v1)" value={gatewayUrl} autoComplete="gateway-url" onChange={event => setGatewayUrl(event.target.value)} /> |                       <CInput | ||||||
|  |                         invalid={emptyGateway} | ||||||
|  |                         type="text" | ||||||
|  |                         required | ||||||
|  |                         placeholder={placeholderUrl} | ||||||
|  |                         value={gatewayUrl} | ||||||
|  |                         autoComplete="gateway-url" | ||||||
|  |                         onChange={(event) => setGatewayUrl(event.target.value)} | ||||||
|  |                       /> | ||||||
|                       <CInvalidFeedback className="help-block"> |                       <CInvalidFeedback className="help-block"> | ||||||
|                         Please enter a gateway url |                         Please enter a gateway url | ||||||
|                       </CInvalidFeedback> |                       </CInvalidFeedback> | ||||||
|                     </CInputGroup> |                     </CInputGroup> | ||||||
|                     <CRow> |                     <CRow> | ||||||
|                       <CCol> |                       <CCol> | ||||||
| 										<CAlert show = { error } color="danger"> |                         <CAlert show={hadError} color="danger"> | ||||||
| 											Login error, confirm that your username, password and gateway url are valid |                           {loginErrorText} | ||||||
|                         </CAlert> |                         </CAlert> | ||||||
|                       </CCol> |                       </CCol> | ||||||
|                     </CRow> |                     </CRow> | ||||||
|                     <CRow> |                     <CRow> | ||||||
|                       <CCol xs="6"> |                       <CCol xs="6"> | ||||||
| 										<CButton color="primary" className="px-4" onClick={event => formValidation() ? SignIn({ userId, password }) : null }>Login |                         <CButton | ||||||
|  |                           color="primary" | ||||||
|  |                           className="px-4" | ||||||
|  |                           onClick={() => (formValidation() ? SignIn({ userId, password }) : null)} | ||||||
|  |                         > | ||||||
|  |                           Login | ||||||
|                         </CButton> |                         </CButton> | ||||||
|                       </CCol> |                       </CCol> | ||||||
|                       <CCol xs="6" className="text-right"> |                       <CCol xs="6" className="text-right"> | ||||||
| 										<CButton color="link" className="px-0">Forgot password?</CButton> |                         <CButton color="link" className="px-0"> | ||||||
|  |                           Forgot password? | ||||||
|  |                         </CButton> | ||||||
|                       </CCol> |                       </CCol> | ||||||
|                     </CRow> |                     </CRow> | ||||||
|                   </CForm> |                   </CForm> | ||||||
| @@ -153,8 +192,7 @@ const Login = () => { | |||||||
|         </CRow> |         </CRow> | ||||||
|       </CContainer> |       </CContainer> | ||||||
|     </div> |     </div> | ||||||
| 	) |   ); | ||||||
| } | }; | ||||||
|    |  | ||||||
|   export default Login |  | ||||||
|  |  | ||||||
|  | export default Login; | ||||||
|   | |||||||
| @@ -8,32 +8,38 @@ import { | |||||||
|   CSpinner, |   CSpinner, | ||||||
|   CBadge, |   CBadge, | ||||||
|   CCol, |   CCol, | ||||||
|     CRow |   CRow, | ||||||
| } from '@coreui/react'; | } from '@coreui/react'; | ||||||
| import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||||
| import { convertDateToUtc, convertDateFromUtc } from '../utils/helper' | import DatePicker from 'react-widgets/DatePicker'; | ||||||
| import DatePicker from "react-widgets/DatePicker"; |  | ||||||
| import { useSelector } from 'react-redux'; | import { useSelector } from 'react-redux'; | ||||||
| import "react-widgets/styles.css"; | import { convertDateToUtc, convertDateFromUtc } from '../utils/helper'; | ||||||
|  | import 'react-widgets/styles.css'; | ||||||
| import { getToken } from '../utils/authHelper'; | import { getToken } from '../utils/authHelper'; | ||||||
| import axiosInstance from '../utils/axiosInstance'; | import axiosInstance from '../utils/axiosInstance'; | ||||||
|  |  | ||||||
| const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, action, extraParameters }) => { | const ActionModalWidget = ({ | ||||||
|  |   show, | ||||||
|  |   toggleModal, | ||||||
|  |   title, | ||||||
|  |   directions, | ||||||
|  |   actionLabel, | ||||||
|  |   action, | ||||||
|  |   extraParameters, | ||||||
|  | }) => { | ||||||
|   const [hadSuccess, setHadSuccess] = useState(false); |   const [hadSuccess, setHadSuccess] = useState(false); | ||||||
|   const [hadFailure, setHadFailure] = useState(false); |   const [hadFailure, setHadFailure] = useState(false); | ||||||
|   const [waiting, setWaiting] = useState(false); |   const [waiting, setWaiting] = useState(false); | ||||||
|   const [chosenDate, setChosenDate] = useState(null); |   const [chosenDate, setChosenDate] = useState(null); | ||||||
|   const [responseBody, setResponseBody] = useState(''); |   const [responseBody, setResponseBody] = useState(''); | ||||||
|     const selectedDeviceId = useSelector(state => state.selectedDeviceId); |   const selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|  |  | ||||||
|     const formValidation = () => { |   const formValidation = () => chosenDate !== null; | ||||||
|         return (chosenDate !== null); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|   const setDateToNow = () => { |   const setDateToNow = () => { | ||||||
|         const now = (new Date()).toISOString(); |     const now = new Date().toISOString(); | ||||||
|     setChosenDate(now); |     setChosenDate(now); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const setDateToLate = () => { |   const setDateToLate = () => { | ||||||
|     const date = convertDateToUtc(new Date()); |     const date = convertDateToUtc(new Date()); | ||||||
| @@ -44,13 +50,13 @@ const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, | |||||||
|     date.setMinutes(0); |     date.setMinutes(0); | ||||||
|  |  | ||||||
|     setChosenDate(convertDateFromUtc(date).toISOString()); |     setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   const setDate = (date) => { |   const setDate = (date) => { | ||||||
|     if (date) { |     if (date) { | ||||||
|       setChosenDate(convertDateFromUtc(date).toISOString()); |       setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|     } |     } | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     setHadSuccess(false); |     setHadSuccess(false); | ||||||
| @@ -68,24 +74,25 @@ const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, | |||||||
|     const token = getToken(); |     const token = getToken(); | ||||||
|  |  | ||||||
|     const headers = { |     const headers = { | ||||||
| 			'Accept': 'application/json', |       Accept: 'application/json', | ||||||
| 			'Authorization': `Bearer ${token}`, |       Authorization: `Bearer ${token}`, | ||||||
| 			'serialNumber': selectedDeviceId |       serialNumber: selectedDeviceId, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     const necessaryParameters = { |     const necessaryParameters = { | ||||||
|       serialNumber: selectedDeviceId, |       serialNumber: selectedDeviceId, | ||||||
|             when: chosenDate |       when: chosenDate, | ||||||
|         } |     }; | ||||||
|  |  | ||||||
|     const parameters = { ...necessaryParameters, ...extraParameters }; |     const parameters = { ...necessaryParameters, ...extraParameters }; | ||||||
|  |  | ||||||
| 		axiosInstance.post(`/device/${selectedDeviceId}/${action}`, parameters,{ headers: headers}) |     axiosInstance | ||||||
|  |       .post(`/device/${selectedDeviceId}/${action}`, parameters, { headers }) | ||||||
|       .then((response) => { |       .then((response) => { | ||||||
|         setResponseBody(JSON.stringify(response.data, null, 4)); |         setResponseBody(JSON.stringify(response.data, null, 4)); | ||||||
|         setHadSuccess(true); |         setHadSuccess(true); | ||||||
|       }) |       }) | ||||||
| 		.catch(error => { |       .catch((error) => { | ||||||
|         setHadFailure(true); |         setHadFailure(true); | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         console.log(error.response); |         console.log(error.response); | ||||||
| @@ -93,13 +100,10 @@ const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, | |||||||
|       .finally(() => { |       .finally(() => { | ||||||
|         setWaiting(false); |         setWaiting(false); | ||||||
|       }); |       }); | ||||||
|     } |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|         <CModal  |     <CModal show={show} onClose={toggleModal}> | ||||||
|             show={show}  |  | ||||||
|             onClose={toggleModal} |  | ||||||
|         > |  | ||||||
|       <CModalHeader closeButton> |       <CModalHeader closeButton> | ||||||
|         <CModalTitle>{title}</CModalTitle> |         <CModalTitle>{title}</CModalTitle> | ||||||
|       </CModalHeader> |       </CModalHeader> | ||||||
| @@ -107,10 +111,14 @@ const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, | |||||||
|         <h6>{directions}</h6> |         <h6>{directions}</h6> | ||||||
|         <CRow style={{ marginTop: '20px' }}> |         <CRow style={{ marginTop: '20px' }}> | ||||||
|           <CCol> |           <CCol> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton> |             <CButton block color="primary" onClick={() => setDateToNow()}> | ||||||
|  |               Now | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|           <CCol> |           <CCol> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>  |             <CButton block color="primary" onClick={() => setDateToLate()}> | ||||||
|  |               Later tonight | ||||||
|  |             </CButton> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|         <CRow style={{ marginTop: '20px' }}> |         <CRow style={{ marginTop: '20px' }}> | ||||||
| @@ -122,47 +130,47 @@ const ActionModalWidget = ({ show, toggleModal, title, directions, actionLabel, | |||||||
|               selected={Date.parse(chosenDate)} |               selected={Date.parse(chosenDate)} | ||||||
|               includeTime |               includeTime | ||||||
|               selectTime |               selectTime | ||||||
|                             placeholder = 'Select custom date in UTC' |               placeholder="Select custom date in UTC" | ||||||
|               disabled={waiting} |               disabled={waiting} | ||||||
|                             onChange = {date => setDate(date)}   |               onChange={(date) => setDate(date)} | ||||||
|               min={convertDateToUtc(new Date())} |               min={convertDateToUtc(new Date())} | ||||||
|             /> |             /> | ||||||
|           </CCol> |           </CCol> | ||||||
|         </CRow> |         </CRow> | ||||||
|  |  | ||||||
|         <div style={{ marginTop: '25px' }}> |         <div style={{ marginTop: '25px' }}> | ||||||
|                     <p>Device will { actionLabel } at (UTC): <b>{ chosenDate }</b></p> |           <p> | ||||||
|  |             Device will {actionLabel} at (UTC): <b>{chosenDate}</b> | ||||||
|  |           </p> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|                 <div hidden={(!hadSuccess && !hadFailure)}> |         <div hidden={!hadSuccess && !hadFailure}> | ||||||
|                 <div><pre>{responseBody}</pre></div> |           <div> | ||||||
|  |             <pre>{responseBody}</pre> | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </CModalBody> |       </CModalBody> | ||||||
|       <CModalFooter> |       <CModalFooter> | ||||||
|         <CButton |         <CButton | ||||||
|           disabled={waiting} |           disabled={waiting} | ||||||
|           color="primary" |           color="primary" | ||||||
|                     onClick={event => formValidation() ? doAction() : null} |           onClick={() => (formValidation() ? doAction() : null)} | ||||||
|         > |         > | ||||||
|           {waiting ? 'Loading...' : 'Schedule'} {'   '} |           {waiting ? 'Loading...' : 'Schedule'} {'   '} | ||||||
|           <CSpinner hidden={!waiting} component="span" size="sm" /> |           <CSpinner hidden={!waiting} component="span" size="sm" /> | ||||||
| 							<CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill"> |           <CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill"> | ||||||
|             Success |             Success | ||||||
|           </CBadge> |           </CBadge> | ||||||
| 							<CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill"> |           <CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill"> | ||||||
|             Request Failed |             Request Failed | ||||||
|           </CBadge> |           </CBadge> | ||||||
|         </CButton> |         </CButton> | ||||||
|                 <CButton  |         <CButton color="secondary" onClick={toggleModal}> | ||||||
|                     color="secondary"  |  | ||||||
|                     onClick={ toggleModal } |  | ||||||
|                 > |  | ||||||
|           Cancel |           Cancel | ||||||
|         </CButton> |         </CButton> | ||||||
|       </CModalFooter> |       </CModalFooter> | ||||||
|     </CModal> |     </CModal> | ||||||
|   ); |   ); | ||||||
|      | }; | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionModalWidget; | export default ActionModalWidget; | ||||||
		Reference in New Issue
	
	Block a user
	 bourquecharles
					bourquecharles