mirror of
				https://github.com/optim-enterprises-bv/OptimCloud-gw-ui.git
				synced 2025-10-30 17:57:46 +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" | ||||||
|   }, |   }, | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								src/App.js
									
									
									
									
									
								
							| @@ -5,33 +5,37 @@ 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(() => { | ||||||
|         const token = sessionStorage.getItem('access_token'); |     const token = sessionStorage.getItem('access_token'); | ||||||
|         if (token !== undefined && token !== null) { |     if (token !== undefined && token !== null) { | ||||||
|             dispatch({type: 'set', connected: true}); |       dispatch({ type: 'set', connected: true }); | ||||||
|         } |     } | ||||||
|     }, [dispatch]); |   }, [dispatch]); | ||||||
|  |  | ||||||
|     return ( |   return ( | ||||||
|         <HashRouter> |     <HashRouter> | ||||||
|                 <React.Suspense fallback={loading}> |       <React.Suspense fallback={loading}> | ||||||
|                 <Switch> |         <Switch> | ||||||
|                     <Route path="/" name="Devices" render={props => isLoggedIn ?  <TheLayout {...props}/> : <Login {...props}/>} /> |           <Route | ||||||
|                 </Switch> |             path="/" | ||||||
|                 </React.Suspense> |             name="Devices" | ||||||
|             </HashRouter> |             render={(props) => (isLoggedIn ? <TheLayout {...props} /> : <Login {...props} />)} | ||||||
|     ); |           /> | ||||||
|  |         </Switch> | ||||||
|  |       </React.Suspense> | ||||||
|  |     </HashRouter> | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export default App; | export default App; | ||||||
| @@ -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,127 +1,70 @@ | |||||||
| 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'; | ||||||
|  |  | ||||||
| const DeviceActions = () => { | 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) => { |   return ( | ||||||
| 		if(!validField){ |     <CCard> | ||||||
| 			setValidField(true); |       <CCardHeader>Device Actions</CCardHeader> | ||||||
| 		} |       <CCardBody> | ||||||
| 		setFirmwareUri(fieldValue); |         <CRow> | ||||||
| 	} |           <CCol> | ||||||
|  |             <CButton block onClick={toggleRebootModal} color="primary"> | ||||||
|  |               Reboot | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block onClick={toggleBlinkModal} color="primary"> | ||||||
|  |               Blink | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |         <CRow style={{ marginTop: '10px' }}> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block color="primary" onClick={toggleUpgradeModal}> | ||||||
|  |               Firmware Upgrade | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol /> | ||||||
|  |         </CRow> | ||||||
|  |       </CCardBody> | ||||||
|  |       <ActionModalWidget | ||||||
|  |         show={showRebootModal} | ||||||
|  |         toggleModal={toggleRebootModal} | ||||||
|  |         title="Reboot Device" | ||||||
|  |         directions="When would you like to reboot this device?" | ||||||
|  |         actionLabel="reboot" | ||||||
|  |         action="reboot" | ||||||
|  |       /> | ||||||
|  |       <ActionModalWidget | ||||||
|  |         show={showBlinkModal} | ||||||
|  |         toggleModal={toggleBlinkModal} | ||||||
|  |         title="Blink LEDs of Device" | ||||||
|  |         directions="When would you like make the LEDs of this device blink?" | ||||||
|  |         actionLabel="blink" | ||||||
|  |         action="leds" | ||||||
|  |         extraParameters={{ duration: 10, pattern: 'on' }} | ||||||
|  |       /> | ||||||
|  |       <FirmwareUpgradeModal show={showUpgradeModal} toggleModal={setShowUpgradeModal} /> | ||||||
|  |     </CCard> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
| 	const formValidation = () => { | export default DeviceActions; | ||||||
| 		if (firmwareUri.trim() === ''){ |  | ||||||
| 			setValidField(false); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 		return true; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	return ( |  | ||||||
| 		<CCard> |  | ||||||
| 			<CCardHeader> |  | ||||||
| 					Device Actions |  | ||||||
| 			</CCardHeader> |  | ||||||
| 			<CCardBody> |  | ||||||
| 				<CRow> |  | ||||||
| 					<CCol> |  | ||||||
| 						<CButton block onClick = { toggleRebootModal } color="primary">Reboot</CButton> |  | ||||||
| 					</CCol> |  | ||||||
| 					<CCol> |  | ||||||
| 						<CButton block onClick = { toggleBlinkModal } color="primary">Blink</CButton> |  | ||||||
| 					</CCol> |  | ||||||
| 				</CRow> |  | ||||||
| 				<CRow style={{marginTop :'10px'}}> |  | ||||||
| 					<CCol> |  | ||||||
| 						<CButton block color="primary" onClick = { toggleUpgradeModal }>Firmware Upgrade</CButton> |  | ||||||
| 					</CCol> |  | ||||||
| 					<CCol> |  | ||||||
| 					</CCol> |  | ||||||
| 				</CRow> |  | ||||||
| 			</CCardBody> |  | ||||||
| 			<ActionModalWidget |  | ||||||
| 				show={showRebootModal} |  | ||||||
| 				toggleModal={toggleRebootModal} |  | ||||||
| 				title='Reboot Device' |  | ||||||
| 				directions='When would you like to reboot this device?' |  | ||||||
| 				actionLabel='reboot' |  | ||||||
| 				action='reboot' |  | ||||||
| 			/> |  | ||||||
| 			<ActionModalWidget |  | ||||||
| 				show={showBlinkModal} |  | ||||||
| 				toggleModal={toggleBlinkModal} |  | ||||||
| 				title='Blink LEDs of Device' |  | ||||||
| 				directions='When would you like make the LEDs of this device blink?' |  | ||||||
| 				actionLabel='blink' |  | ||||||
| 				action='leds' |  | ||||||
| 				extraParameters= {{ duration : 10, pattern : 'on' }} |  | ||||||
| 			/> |  | ||||||
| 			<FirmwareUpgradeModal |  | ||||||
| 				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> |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default DeviceActions |  | ||||||
|   | |||||||
| @@ -1,178 +1,169 @@ | |||||||
| import React, { useState, useEffect } from 'react' | import React, { useState, useEffect } from 'react'; | ||||||
| import { | import { | ||||||
|     CCard, |   CCard, | ||||||
|     CCardHeader, |   CCardHeader, | ||||||
|     CCardBody, |   CCardBody, | ||||||
|     CFormGroup, |   CFormGroup, | ||||||
|     CCol, |   CCol, | ||||||
|     CLabel, |   CLabel, | ||||||
|     CForm, |   CForm, | ||||||
|     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 | ||||||
|         .then((response) => { |       .get(`/device/${selectedDeviceId}`, options) | ||||||
|             setDevice(response.data); |       .then((response) => { | ||||||
|             setLoading(false); |         setDevice(response.data); | ||||||
|         }) |       }) | ||||||
|         .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){ |  | ||||||
|         return ( |  | ||||||
|             <CCard> |  | ||||||
|                 <CCardHeader> |  | ||||||
|                     #{device.serialNumber} Details |  | ||||||
|                 </CCardHeader> |  | ||||||
|                 <CCardBody> |  | ||||||
|                     <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>UUID : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {device.UUID } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>Serial Number : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {device.serialNumber } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Device Type : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.deviceType } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>Last Configuration Change : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {cleanTimestamp(device.lastConfigurationChange) } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>MAC address :</CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 { device.macAddress } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CCollapse show={collapse}> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Created : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { cleanTimestamp(device.createdTimestamp) } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Last Configuration Download : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     {cleanTimestamp(device.lastConfigurationDownload) } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Manufacturer :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.manufacturer } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel htmlFor="text-input">Notes :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     <CInput id="text-input" name="text-input" placeholder={device.notes} /> |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Owner :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.owner } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Location :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.location } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                         </CCollapse> |  | ||||||
|                         <CCardFooter> |  | ||||||
|                             <CButton show={collapse} color="transparent" onClick = { toggle } block> |  | ||||||
|                                 <CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} size="lg"/> |  | ||||||
|                             </CButton> |  | ||||||
|                         </CCardFooter> |  | ||||||
|                     </CForm> |  | ||||||
|                 </CCardBody> |  | ||||||
|             </CCard> |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |   if (device) { | ||||||
|     return ( |     return ( | ||||||
|             <CCard> |       <CCard> | ||||||
|                 <CCardHeader> |         <CCardHeader>#{device.serialNumber} Details</CCardHeader> | ||||||
|                     Device Configuration |         <CCardBody> | ||||||
|                 </CCardHeader> |           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> | ||||||
|                 <CCardBody> |             <CFormGroup row> | ||||||
|                 </CCardBody> |               <CCol md="3"> | ||||||
|             </CCard> |                 <CLabel>UUID : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.UUID} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Serial Number : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.serialNumber} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Device Type : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.deviceType} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Last Configuration Change : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {cleanTimestamp(device.lastConfigurationChange)} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>MAC address :</CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.macAddress} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CCollapse show={collapse}> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Created : </CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {cleanTimestamp(device.createdTimestamp)} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Last Configuration Download : </CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {cleanTimestamp(device.lastConfigurationDownload)} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Manufacturer :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.manufacturer} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel htmlFor="text-input">Notes :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   <CInput id="text-input" name="text-input" placeholder={device.notes} /> | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Owner :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.owner} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Location :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.location} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |             </CCollapse> | ||||||
|  |             <CCardFooter> | ||||||
|  |               <CButton show={collapse} color="transparent" onClick={toggle} block> | ||||||
|  |                 <CIcon name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} size="lg" /> | ||||||
|  |               </CButton> | ||||||
|  |             </CCardFooter> | ||||||
|  |           </CForm> | ||||||
|  |         </CCardBody> | ||||||
|  |       </CCard> | ||||||
|     ); |     ); | ||||||
| } |   } | ||||||
|  |  | ||||||
| export default DeviceConfiguration |   return ( | ||||||
|  |     <CCard> | ||||||
|  |       <CCardHeader>Device Configuration</CCardHeader> | ||||||
|  |       <CCardBody /> | ||||||
|  |     </CCard> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default DeviceConfiguration; | ||||||
|   | |||||||
| @@ -1,168 +1,147 @@ | |||||||
| 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'; | ||||||
| import { getToken } from '../utils/authHelper'; | 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 options = { | ||||||
|  |       headers: { | ||||||
|  |         Accept: 'application/json', | ||||||
|  |         Authorization: `Bearer ${getToken()}`, | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     axiosInstance | ||||||
|  |       .get(`/device/${selectedDeviceId}/healthchecks`, options) | ||||||
|  |       .then((response) => { | ||||||
|  |         setHealthChecks(response.data.values); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         console.log(error); | ||||||
|  |         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 getDeviceHealth = () => { |   const columns = [ | ||||||
|         const options = { |     { key: 'UUID', label: 'Config. Id' }, | ||||||
|             headers : { |     { key: 'recorded' }, | ||||||
|                 'Accept': 'application/json', |     { key: 'sanity' }, | ||||||
|                 'Authorization': `Bearer ${getToken()}` |     { | ||||||
|             } |       key: 'show_details', | ||||||
|         }; |       label: '', | ||||||
|  |       _style: { width: '1%' }, | ||||||
|  |       sorter: false, | ||||||
|  |       filter: false, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|         axiosInstance.get(`/device/${selectedDeviceId}/healthchecks`, options) |   useEffect(() => { | ||||||
|         .then((response) => { |     getDeviceHealth(); | ||||||
|             setHealthChecks(response.data.values); |   }, []); | ||||||
|             setLoading(false); |  | ||||||
|         }) |  | ||||||
|         .catch(error => { |  | ||||||
|             setLoading(false); |  | ||||||
|             console.log(error); |  | ||||||
|             console.log(error.response); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     //Function called from the button on the table so that a user can see more details |   if (healthChecks && healthChecks.length > 0) { | ||||||
|     const toggleDetails = (index) => { |     sanityLevel = healthChecks[0].sanity; | ||||||
|         const position = details.indexOf(index) |     if (sanityLevel === 100) barColor = 'gradient-success'; | ||||||
|         let newDetails = details.slice() |     else if (sanityLevel >= 90) barColor = 'gradient-warning'; | ||||||
|  |     else barColor = 'gradient-danger'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|         if (position !== -1) { |   return ( | ||||||
|             newDetails.splice(position, 1) |     <CWidgetProgress | ||||||
|         } |       header={sanityLevel ? `${sanityLevel}%` : 'Unknown'} | ||||||
|         else { |       text="Device Health" | ||||||
|             newDetails = [...details, index] |       value={sanityLevel ?? 100} | ||||||
|         } |       color={barColor} | ||||||
|         setDetails(newDetails) |       inverse | ||||||
|     } |       footer={ | ||||||
|  |         <div> | ||||||
|  |           <CCollapse show={collapse}> | ||||||
|  |             <CCard> | ||||||
|  |               <div className="overflow-auto" style={{ height: '250px' }}> | ||||||
|  |                 <CDataTable | ||||||
|  |                   items={healthChecks ?? []} | ||||||
|  |                   fields={columns} | ||||||
|  |                   style={{ color: 'white' }} | ||||||
|  |                   sorterValue={{ column: 'recorded', desc: 'true' }} | ||||||
|  |                   scopedSlots={{ | ||||||
|  |                     recorded: (item) => <td>{cleanTimestamp(item.recorded)}</td>, | ||||||
|  |                     sanity: (item) => <td>{`${item.sanity}%`}</td>, | ||||||
|  |                     show_details: (item, index) => { | ||||||
|  |                       if (item.sanity === 100) { | ||||||
|  |                         return <></>; | ||||||
|  |                       } | ||||||
|  |                       return ( | ||||||
|  |                         <td className="py-2"> | ||||||
|  |                           <CButton | ||||||
|  |                             color="primary" | ||||||
|  |                             variant="outline" | ||||||
|  |                             shape="square" | ||||||
|  |                             size="sm" | ||||||
|  |                             onClick={() => { | ||||||
|  |                               toggleDetails(index); | ||||||
|  |                             }} | ||||||
|  |                           > | ||||||
|  |                             {details.includes(index) ? 'Hide' : 'Show'} | ||||||
|  |                           </CButton> | ||||||
|  |                         </td> | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                     details: (item, index) => ( | ||||||
|  |                       <CCollapse show={details.includes(index)}> | ||||||
|  |                         <CCardBody> | ||||||
|  |                           <h5>Details</h5> | ||||||
|  |                           <div> | ||||||
|  |                             <pre>{JSON.stringify(item.values, null, 4)}</pre> | ||||||
|  |                           </div> | ||||||
|  |                         </CCardBody> | ||||||
|  |                       </CCollapse> | ||||||
|  |                     ), | ||||||
|  |                   }} | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |             </CCard> | ||||||
|  |           </CCollapse> | ||||||
|  |           <CButton show={collapse} color="transparent" onClick={toggle} block> | ||||||
|  |             <CIcon | ||||||
|  |               name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} | ||||||
|  |               style={{ color: 'white' }} | ||||||
|  |               size="lg" | ||||||
|  |             /> | ||||||
|  |           </CButton> | ||||||
|  |         </div> | ||||||
|  |       } | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|     const columns = [ | export default DeviceHealth; | ||||||
|         { key: 'UUID', label: 'Config. Id'}, |  | ||||||
|         { key: 'recorded'}, |  | ||||||
|         { key: 'sanity'}, |  | ||||||
|         { |  | ||||||
|             key: 'show_details', |  | ||||||
|             label: '', |  | ||||||
|             _style: { width: '1%' }, |  | ||||||
|             sorter: false, |  | ||||||
|             filter: false |  | ||||||
|           } |  | ||||||
|     ]; |  | ||||||
|  |  | ||||||
|     useEffect(() => { |  | ||||||
|         setLoading(true); |  | ||||||
|         getDeviceHealth(); |  | ||||||
|     },[]); |  | ||||||
|  |  | ||||||
|     if(healthChecks && healthChecks.length > 0){ |  | ||||||
|         sanityLevel = healthChecks[0].sanity; |  | ||||||
|         if(sanityLevel === 100) |  | ||||||
|             barColor = "gradient-success"; |  | ||||||
|         else if (sanityLevel >= 90) |  | ||||||
|             barColor = "gradient-warning"; |  | ||||||
|         else |  | ||||||
|             barColor = "gradient-danger"; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     return ( |  | ||||||
|         <CWidgetProgress |  | ||||||
|             header={sanityLevel ? `${sanityLevel}%` : 'Unknown'} |  | ||||||
|             text="Device Health" |  | ||||||
|             value={sanityLevel ?? 100} |  | ||||||
|             color={barColor} |  | ||||||
|             inverse |  | ||||||
|             footer={ |  | ||||||
|                 <div> |  | ||||||
|                     <CCollapse show={collapse}> |  | ||||||
|                         <CCard> |  | ||||||
|                         <div className="overflow-auto" style={{height: '250px'}}> |  | ||||||
|                             <CDataTable |  | ||||||
|                                 items={healthChecks ?? [] } |  | ||||||
|                                 fields={columns} |  | ||||||
|                                 style={{color: 'white'}} |  | ||||||
|                                 sorterValue={{column: 'recorded', desc:'true'}} |  | ||||||
|                                 scopedSlots = {{ |  | ||||||
|                                     'recorded': |  | ||||||
|                                     (item)=>( |  | ||||||
|                                         <td> |  | ||||||
|                                         {cleanTimestamp(item.recorded)} |  | ||||||
|                                         </td> |  | ||||||
|                                     ), |  | ||||||
|                                     'sanity': |  | ||||||
|                                     (item)=>( |  | ||||||
|                                         <td> |  | ||||||
|                                             {`${item.sanity}%`} |  | ||||||
|                                         </td> |  | ||||||
|                                     ), |  | ||||||
|                                     'show_details': |  | ||||||
|                                     (item, index)=>{ |  | ||||||
|                                         if(item.sanity === 100){ |  | ||||||
|                                             return (<></>); |  | ||||||
|                                         } |  | ||||||
|                                         return ( |  | ||||||
|                                             <td className="py-2"> |  | ||||||
|                                                 <CButton |  | ||||||
|                                                 color="primary" |  | ||||||
|                                                 variant="outline" |  | ||||||
|                                                 shape="square" |  | ||||||
|                                                 size="sm" |  | ||||||
|                                                 onClick={()=>{toggleDetails(index)}} |  | ||||||
|                                                 > |  | ||||||
|                                                 {details.includes(index) ? 'Hide' : 'Show'} |  | ||||||
|                                                 </CButton> |  | ||||||
|                                             </td> |  | ||||||
|                                         ); |  | ||||||
|                                     }, |  | ||||||
|                                     'details': |  | ||||||
|                                         (item, index)=>{ |  | ||||||
|                                             return ( |  | ||||||
|                                                 <CCollapse show={details.includes(index)}> |  | ||||||
|                                                     <CCardBody> |  | ||||||
|                                                         <h5>Details</h5> |  | ||||||
|                                                         <div><pre>{JSON.stringify(item.values, null, 4)}</pre></div> |  | ||||||
|                                                     </CCardBody> |  | ||||||
|                                                 </CCollapse> |  | ||||||
|                                             ); |  | ||||||
|                                     } |  | ||||||
|                                 }} |  | ||||||
|                             > |  | ||||||
|                             </CDataTable> |  | ||||||
|                         </div> |  | ||||||
|                         </CCard> |  | ||||||
|                     </CCollapse> |  | ||||||
|                     <CButton show={collapse} color="transparent" onClick = { toggle } block> |  | ||||||
|                         <CIcon name={collapse ? "cilChevronTop" : "cilChevronBottom"} style={{color: 'white'}} size="lg"/> |  | ||||||
|                     </CButton> |  | ||||||
|                 </div> |  | ||||||
|             } |  | ||||||
|         > |  | ||||||
|         </CWidgetProgress> |  | ||||||
|     ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default DeviceHealth |  | ||||||
|   | |||||||
| @@ -1,328 +1,275 @@ | |||||||
| import React, { useEffect, useState } from 'react' | import React, { useEffect, useState } from 'react'; | ||||||
| import { | import { | ||||||
| 	CBadge, |   CBadge, | ||||||
| 	CCardBody, |   CCardBody, | ||||||
| 	CDataTable, |   CDataTable, | ||||||
| 	CButton, |   CButton, | ||||||
| 	CLink, |   CLink, | ||||||
| 	CCard, |   CCard, | ||||||
| 	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); | ||||||
| 	const [serialNumbers, setSerialNumbers] = useState([]); |   const [serialNumbers, setSerialNumbers] = useState([]); | ||||||
| 	const [page, setPage] = useState(0); |   const [page, setPage] = useState(0); | ||||||
| 	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 = () => { | ||||||
| 		const token = getToken(); |     const token = getToken(); | ||||||
| 		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) => { |       }) | ||||||
| 			setSerialNumbers(response.data.serialNumbers); |       .then((response) => { | ||||||
| 			setLoadedSerials(true); |         setSerialNumbers(response.data.serialNumbers); | ||||||
| 		}) |         setLoadedSerials(true); | ||||||
| 		.catch(error => { |       }) | ||||||
| 			setLoading(false); |       .catch((error) => { | ||||||
| 			console.log(error.response); |         setLoading(false); | ||||||
| 		}); |         console.log(error.response); | ||||||
| 	}; |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
| 	const getDeviceInformation = () => { |   const getDeviceInformation = () => { | ||||||
| 		const token = getToken(); |     const token = getToken(); | ||||||
| 		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) => { |       }) | ||||||
| 			setDevices(response.data.devicesWithStatus); |       .then((response) => { | ||||||
| 			setLoading(false); |         setDevices(response.data.devicesWithStatus); | ||||||
| 		}) |         setLoading(false); | ||||||
| 		.catch(error => { |       }) | ||||||
| 			setLoading(false); |       .catch((error) => { | ||||||
| 			console.log(error.response); |         setLoading(false); | ||||||
| 		}); |         console.log(error.response); | ||||||
| 	} |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
| 	//Function called from the button on the table so that a user can see more details |   const updateDevicesPerPage = (value) => { | ||||||
| 	const toggleDetails = (index) => { |     setDevicesPerPage(value); | ||||||
| 		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 updatePageCount = ({ selected: selectedPage }) => { | ||||||
| 		setDevicesPerPage(value); |     setPage(selectedPage); | ||||||
| 	} |   }; | ||||||
|  |  | ||||||
| 	const updatePageCount = ({ selected: selectedPage }) => { |   // Initial load | ||||||
| 		setPage(selectedPage); |   useEffect(() => { | ||||||
| 	} |     getSerialNumbers(); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
| 	//Initial load |   // Updating the devices only if serial numbers, page number or devices per page changes | ||||||
| 	useEffect(() => { |   useEffect(() => { | ||||||
| 		getSerialNumbers(); |     if (loadedSerials) getDeviceInformation(); | ||||||
| 	}, []); |   }, [serialNumbers, page, devicesPerPage, loadedSerials]); | ||||||
|  |  | ||||||
| 	//Updating the devices only if serial numbers, page number or devices per page changes |   useEffect(() => { | ||||||
| 	useEffect(()=>{ |     if (loadedSerials) { | ||||||
| 		if(loadedSerials) getDeviceInformation(); |       const count = Math.ceil(serialNumbers.length / devicesPerPage); | ||||||
|     }, [serialNumbers, page, devicesPerPage, loadedSerials]); |       setPageCount(count); | ||||||
|  |     } | ||||||
| 	useEffect(() => { |   }, [devicesPerPage, loadedSerials]); | ||||||
| 		if(loadedSerials) { |  | ||||||
| 			const count = Math.ceil(serialNumbers.length / devicesPerPage); |  | ||||||
| 			setPageCount(count); |  | ||||||
| 		}  |  | ||||||
| 	}, [devicesPerPage, loadedSerials]); |  | ||||||
|  |  | ||||||
|   	return ( |  | ||||||
| 		<DeviceListDisplay  |  | ||||||
| 			devices={devices}  |  | ||||||
| 			toggleDetails={toggleDetails}  |  | ||||||
| 			details={details} |  | ||||||
| 			loading={loading}  |  | ||||||
| 			updateDevicesPerPage={updateDevicesPerPage} |  | ||||||
| 			pageCount={pageCount} |  | ||||||
| 			updatePage={updatePageCount} |  | ||||||
| 			pageRangeDisplayed={5} |  | ||||||
| 		/> |  | ||||||
|   	); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const DeviceListDisplay = ({ devices, toggleDetails, details, loading, updateDevicesPerPage, pageCount, updatePage }) => { |  | ||||||
|   const columns = [ |  | ||||||
| 	{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' },}, |  | ||||||
| 	{ key: 'serialNumber', _style: { width: '5%' }}, |  | ||||||
| 	{ key: 'UUID', label: 'Config Id', _style: { width: '5%' }}, |  | ||||||
| 	{ key: 'firmware', filter: false, _style: { width: '20%' }, }, |  | ||||||
| 	{ key: 'manufacturer', filter: false, _style: { width: '20%' }, }, |  | ||||||
| 	{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } }, |  | ||||||
| 	{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } }, |  | ||||||
| 	{ key: 'ipAddress', _style: { width: '20%' }}, |  | ||||||
| 	{ |  | ||||||
| 	  key: 'show_details', |  | ||||||
| 	  label: '', |  | ||||||
| 	  _style: { width: '3%' }, |  | ||||||
| 	  sorter: false, |  | ||||||
| 	  filter: false |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		key: 'refresh', |  | ||||||
| 		label: '', |  | ||||||
| 		_style: { width: '2%' }, |  | ||||||
| 		sorter: false, |  | ||||||
| 		filter: false |  | ||||||
| 	  } |  | ||||||
|   ]; |  | ||||||
|  |  | ||||||
|   const selectOptions = [ |  | ||||||
| 	{ value: '10', label: '10' }, |  | ||||||
| 	{ value: '25', label: '25' }, |  | ||||||
| 	{ value: '50', label: '50' } |  | ||||||
|   ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   const getDeviceIcon = (deviceType) =>{ |  | ||||||
| 	if(deviceType === "AP_Default"){ |  | ||||||
| 	  return <CIcon name="cilRouter" size="2xl" alt='AP'></CIcon>; |  | ||||||
| 	} |  | ||||||
| 	else if(deviceType === "IOT"){ |  | ||||||
| 	  return <img src={iotIcon} style={{height:'32px', width:'32px'}} alt='IOT'/>; |  | ||||||
| 	} |  | ||||||
| 	else if(deviceType === "SWITCH"){ |  | ||||||
| 	  return <img src={internetSwitch} style={{height:'32px', width:'32px'}} alt='SWITCH'/>; |  | ||||||
| 	} |  | ||||||
| 	return null; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const getStatusBadge = (status) => { |  | ||||||
| 	if(status){ |  | ||||||
| 	  return 'success'; |  | ||||||
| 	} |  | ||||||
| 	return 'danger'; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| 	<> |     <DeviceListDisplay | ||||||
| 	  <CCard> |       devices={devices} | ||||||
| 		<CCardHeader> |       loading={loading} | ||||||
| 			<CRow> |       updateDevicesPerPage={updateDevicesPerPage} | ||||||
| 				<CCol> |       pageCount={pageCount} | ||||||
| 				</CCol> |       updatePage={updatePageCount} | ||||||
| 				<CCol xs={2}> |       pageRangeDisplayed={5} | ||||||
| 					<Select  |     /> | ||||||
| 						isClearable = { false }  |  | ||||||
| 						options = { selectOptions }  |  | ||||||
| 						defaultValue = {{ value : '10', label: '10' }}   |  | ||||||
| 						onChange={value => updateDevicesPerPage(value.value)} |  | ||||||
| 					/> |  | ||||||
| 				</CCol> |  | ||||||
| 			</CRow> |  | ||||||
| 		</CCardHeader> |  | ||||||
| 		<CCardBody> |  | ||||||
| 		  <CDataTable |  | ||||||
| 			items={devices} |  | ||||||
| 			fields={columns} |  | ||||||
| 			border |  | ||||||
| 			hover |  | ||||||
| 			loading = {loading} |  | ||||||
| 			scopedSlots = {{ |  | ||||||
| 				'deviceType': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td style={{textAlign: 'center'}}> |  | ||||||
| 						<CPopover |  | ||||||
| 							content={ item.connected ? 'Connected' : 'Not Connected' } |  | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<CBadge color={getStatusBadge(item.connected)}> |  | ||||||
| 								{getDeviceIcon(item.deviceType) ?? item.deviceType} |  | ||||||
| 							</CBadge> |  | ||||||
| 						</CPopover> |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'firmware': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td> |  | ||||||
| 						<CPopover |  | ||||||
| 							content={ item.firmware ? item.firmware : 'N/A' } |  | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<p> |  | ||||||
| 								{cropStringWithEllipsis(item.firmware, 22)} |  | ||||||
| 							</p> |  | ||||||
| 						</CPopover> |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'manufacturer': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td> |  | ||||||
| 						<CPopover |  | ||||||
| 							content={ item.manufacturer ? item.manufacturer : 'N/A' } |  | ||||||
| 							placement="top" |  | ||||||
| 						> |  | ||||||
| 							<p> |  | ||||||
| 								{cropStringWithEllipsis(item.manufacturer, 23)} |  | ||||||
| 							</p> |  | ||||||
| 						</CPopover> |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'txBytes': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td> |  | ||||||
| 					{cleanBytesString(item.txBytes)} |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'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> |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'refresh': |  | ||||||
| 				(item)=>( |  | ||||||
| 					<td className="py-2"> |  | ||||||
| 						<CButton color="primary" variant="outline" size="sm">					 |  | ||||||
| 							<CIcon name="cil-sync" content={cilSync} size="sm"/> |  | ||||||
| 						</CButton> |  | ||||||
| 					</td> |  | ||||||
| 				), |  | ||||||
| 				'show_details': |  | ||||||
| 				(item, index)=>{ |  | ||||||
| 					return ( |  | ||||||
| 					<td className="py-2"> |  | ||||||
| 						<CLink  |  | ||||||
| 							className="c-subheader-nav-link"  |  | ||||||
| 							aria-current="page"  |  | ||||||
| 							to={() => `/devices/${item.serialNumber}`} |  | ||||||
| 						>  |  | ||||||
| 							<CButton |  | ||||||
| 								color="primary" |  | ||||||
| 								variant="outline" |  | ||||||
| 								shape="square" |  | ||||||
| 								size="sm" |  | ||||||
| 							> |  | ||||||
| 								<CIcon name="cil-info" content={cilInfo} size="l"/> |  | ||||||
| 							</CButton> |  | ||||||
| 						</CLink> |  | ||||||
| 					</td> |  | ||||||
| 					) |  | ||||||
| 				} |  | ||||||
| 			}}/> |  | ||||||
| 		  	<ReactPaginate |  | ||||||
| 				previousLabel={"← Previous"} |  | ||||||
| 				nextLabel={"Next →"} |  | ||||||
| 				pageCount={pageCount} |  | ||||||
| 				onPageChange={updatePage}  |  | ||||||
| 				breakClassName={'page-item'} |  | ||||||
| 				breakLinkClassName={'page-link'} |  | ||||||
| 				containerClassName={'pagination'} |  | ||||||
| 				pageClassName={'page-item'} |  | ||||||
| 				pageLinkClassName={'page-link'} |  | ||||||
| 				previousClassName={'page-item'} |  | ||||||
| 				previousLinkClassName={'page-link'} |  | ||||||
| 				nextClassName={'page-item'} |  | ||||||
| 				nextLinkClassName={'page-link'} |  | ||||||
| 				activeClassName={'active'} |  | ||||||
| 			/> |  | ||||||
| 		</CCardBody> |  | ||||||
| 	  </CCard> |  | ||||||
| 	</> |  | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | const DeviceListDisplay = ({ devices, loading, updateDevicesPerPage, pageCount, updatePage }) => { | ||||||
|  |   const columns = [ | ||||||
|  |     { key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' } }, | ||||||
|  |     { key: 'serialNumber', _style: { width: '5%' } }, | ||||||
|  |     { key: 'UUID', label: 'Config Id', _style: { width: '5%' } }, | ||||||
|  |     { key: 'firmware', filter: false, _style: { width: '20%' } }, | ||||||
|  |     { key: 'manufacturer', filter: false, _style: { width: '20%' } }, | ||||||
|  |     { key: 'txBytes', label: 'Tx', filter: false, _style: { width: '10%' } }, | ||||||
|  |     { key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '10%' } }, | ||||||
|  |     { key: 'ipAddress', _style: { width: '20%' } }, | ||||||
|  |     { | ||||||
|  |       key: 'show_details', | ||||||
|  |       label: '', | ||||||
|  |       _style: { width: '3%' }, | ||||||
|  |       sorter: false, | ||||||
|  |       filter: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'refresh', | ||||||
|  |       label: '', | ||||||
|  |       _style: { width: '2%' }, | ||||||
|  |       sorter: false, | ||||||
|  |       filter: false, | ||||||
|  |     }, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   const selectOptions = [ | ||||||
|  |     { value: '10', label: '10' }, | ||||||
|  |     { value: '25', label: '25' }, | ||||||
|  |     { value: '50', label: '50' }, | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   const getDeviceIcon = (deviceType) => { | ||||||
|  |     if (deviceType === 'AP_Default') { | ||||||
|  |       return <CIcon name="cilRouter" size="2xl" alt="AP" />; | ||||||
|  |     } | ||||||
|  |     if (deviceType === 'IOT') { | ||||||
|  |       return <img src={iotIcon} style={{ height: '32px', width: '32px' }} alt="IOT" />; | ||||||
|  |     } | ||||||
|  |     if (deviceType === 'SWITCH') { | ||||||
|  |       return <img src={internetSwitch} style={{ height: '32px', width: '32px' }} alt="SWITCH" />; | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const getStatusBadge = (status) => { | ||||||
|  |     if (status) { | ||||||
|  |       return 'success'; | ||||||
|  |     } | ||||||
|  |     return 'danger'; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <CCard> | ||||||
|  |         <CCardHeader> | ||||||
|  |           <CRow> | ||||||
|  |             <CCol /> | ||||||
|  |             <CCol xs={2}> | ||||||
|  |               <Select | ||||||
|  |                 isClearable={false} | ||||||
|  |                 options={selectOptions} | ||||||
|  |                 defaultValue={{ value: '10', label: '10' }} | ||||||
|  |                 onChange={(value) => updateDevicesPerPage(value.value)} | ||||||
|  |               /> | ||||||
|  |             </CCol> | ||||||
|  |           </CRow> | ||||||
|  |         </CCardHeader> | ||||||
|  |         <CCardBody> | ||||||
|  |           <CDataTable | ||||||
|  |             items={devices} | ||||||
|  |             fields={columns} | ||||||
|  |             border | ||||||
|  |             hover | ||||||
|  |             loading={loading} | ||||||
|  |             scopedSlots={{ | ||||||
|  |               deviceType: (item) => ( | ||||||
|  |                 <td style={{ textAlign: 'center' }}> | ||||||
|  |                   <CPopover | ||||||
|  |                     content={item.connected ? 'Connected' : 'Not Connected'} | ||||||
|  |                     placement="top" | ||||||
|  |                   > | ||||||
|  |                     <CBadge color={getStatusBadge(item.connected)}> | ||||||
|  |                       {getDeviceIcon(item.deviceType) ?? item.deviceType} | ||||||
|  |                     </CBadge> | ||||||
|  |                   </CPopover> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |               firmware: (item) => ( | ||||||
|  |                 <td> | ||||||
|  |                   <CPopover content={item.firmware ? item.firmware : 'N/A'} placement="top"> | ||||||
|  |                     <p>{cropStringWithEllipsis(item.firmware, 22)}</p> | ||||||
|  |                   </CPopover> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |               manufacturer: (item) => ( | ||||||
|  |                 <td> | ||||||
|  |                   <CPopover content={item.manufacturer ? item.manufacturer : 'N/A'} placement="top"> | ||||||
|  |                     <p>{cropStringWithEllipsis(item.manufacturer, 23)}</p> | ||||||
|  |                   </CPopover> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |               txBytes: (item) => <td>{cleanBytesString(item.txBytes)}</td>, | ||||||
|  |               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> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |               refresh: () => ( | ||||||
|  |                 <td className="py-2"> | ||||||
|  |                   <CButton color="primary" variant="outline" size="sm"> | ||||||
|  |                     <CIcon name="cil-sync" content={cilSync} size="sm" /> | ||||||
|  |                   </CButton> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |               show_details: (item) => ( | ||||||
|  |                 <td className="py-2"> | ||||||
|  |                   <CLink | ||||||
|  |                     className="c-subheader-nav-link" | ||||||
|  |                     aria-current="page" | ||||||
|  |                     to={() => `/devices/${item.serialNumber}`} | ||||||
|  |                   > | ||||||
|  |                     <CButton color="primary" variant="outline" shape="square" size="sm"> | ||||||
|  |                       <CIcon name="cil-info" content={cilInfo} size="l" /> | ||||||
|  |                     </CButton> | ||||||
|  |                   </CLink> | ||||||
|  |                 </td> | ||||||
|  |               ), | ||||||
|  |             }} | ||||||
|  |           /> | ||||||
|  |           <ReactPaginate | ||||||
|  |             previousLabel="← Previous" | ||||||
|  |             nextLabel="Next →" | ||||||
|  |             pageCount={pageCount} | ||||||
|  |             onPageChange={updatePage} | ||||||
|  |             breakClassName="page-item" | ||||||
|  |             breakLinkClassName="page-link" | ||||||
|  |             containerClassName="pagination" | ||||||
|  |             pageClassName="page-item" | ||||||
|  |             pageLinkClassName="page-link" | ||||||
|  |             previousClassName="page-item" | ||||||
|  |             previousLinkClassName="page-link" | ||||||
|  |             nextClassName="page-item" | ||||||
|  |             nextLinkClassName="page-link" | ||||||
|  |             activeClassName="active" | ||||||
|  |           /> | ||||||
|  |         </CCardBody> | ||||||
|  |       </CCard> | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
| export default DeviceList; | export default DeviceList; | ||||||
| @@ -1,168 +1,160 @@ | |||||||
| import React from 'react' | import React from 'react'; | ||||||
| import { | import { | ||||||
|     CCard, |   CCard, | ||||||
|     CCardHeader, |   CCardHeader, | ||||||
|     CCardBody, |   CCardBody, | ||||||
|     CFormGroup, |   CFormGroup, | ||||||
|     CCol, |   CCol, | ||||||
|     CLabel, |   CLabel, | ||||||
|     CForm, |   CForm, | ||||||
|     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 ( |  | ||||||
|             <CWidgetProgressIcon |  | ||||||
|                 header={sanityLevel ? `${sanityLevel}%` : 'Unknown'} |  | ||||||
|                 text="Device Health" |  | ||||||
|                 value={sanityLevel ?? 100} |  | ||||||
|                 color={barColor} |  | ||||||
|                 inverse |  | ||||||
|             > |  | ||||||
|                 <CDropdown> |  | ||||||
|                     {/* Need inline styling because CDropdownToggle does not take into account the |  | ||||||
|                     parent's inverse value*/} |  | ||||||
|                     <CDropdownToggle style={{color: 'white'}}> |  | ||||||
|                         <CIcon content={cilSettings}/> |  | ||||||
|                     </CDropdownToggle> |  | ||||||
|                     <CDropdownMenu placement="bottom-end"> |  | ||||||
|                         <CDropdownItem>View Logs</CDropdownItem> |  | ||||||
|                     </CDropdownMenu> |  | ||||||
|                 </CDropdown> |  | ||||||
|             </CWidgetProgressIcon> |  | ||||||
|     ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|      |  | ||||||
|     if(device){ |  | ||||||
|         return ( |  | ||||||
|             <CCard> |  | ||||||
|                 <CCardHeader> |  | ||||||
|                     Device #{device.serialNumber} Configuration |  | ||||||
|                 </CCardHeader> |  | ||||||
|                 <CCardBody> |  | ||||||
|                     <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>UUID : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {device.UUID } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>Serial Number : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {device.serialNumber } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Device Type : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.deviceType } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>Last Configuration Change : </CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 {cleanTimestamp(device.lastConfigurationChange) } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CFormGroup row> |  | ||||||
|                             <CCol md="3"> |  | ||||||
|                                 <CLabel>MAC address :</CLabel> |  | ||||||
|                             </CCol> |  | ||||||
|                             <CCol xs="12" md="9"> |  | ||||||
|                                 { device.macAddress } |  | ||||||
|                             </CCol> |  | ||||||
|                         </CFormGroup> |  | ||||||
|                         <CCollapse show={collapse}> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Created : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { cleanTimestamp(device.createdTimestamp) } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Last Configuration Download : </CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     {cleanTimestamp(device.lastConfigurationDownload) } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Manufacturer :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.manufacturer } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel htmlFor="text-input">Notes :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     <CInput id="text-input" name="text-input" placeholder={device.notes} /> |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Owner :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.owner } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                             <CFormGroup row> |  | ||||||
|                                 <CCol md="3"> |  | ||||||
|                                     <CLabel>Location :</CLabel> |  | ||||||
|                                 </CCol> |  | ||||||
|                                 <CCol xs="12" md="9"> |  | ||||||
|                                     { device.location } |  | ||||||
|                                 </CCol> |  | ||||||
|                             </CFormGroup> |  | ||||||
|                         </CCollapse> |  | ||||||
|                         <CCardFooter> |  | ||||||
|                             <CButton |  | ||||||
|                             color="primary" |  | ||||||
|                             onClick={toggle} |  | ||||||
|                             className={'mb-1'} |  | ||||||
|                             >More details</CButton> |  | ||||||
|                         </CCardFooter> |  | ||||||
|                     </CForm> |  | ||||||
|                 </CCardBody> |  | ||||||
|             </CCard> |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|             <CCard> |       <CWidgetProgressIcon | ||||||
|                 <CCardHeader> |         header={sanityLevel ? `${sanityLevel}%` : 'Unknown'} | ||||||
|                     Device Configuration |         text="Device Health" | ||||||
|                 </CCardHeader> |         value={sanityLevel ?? 100} | ||||||
|                 <CCardBody> |         color={barColor} | ||||||
|                 </CCardBody> |         inverse | ||||||
|             </CCard> |       > | ||||||
|  |         <CDropdown> | ||||||
|  |           {/* Need inline styling because CDropdownToggle does not take into account the | ||||||
|  |                     parent's inverse value */} | ||||||
|  |           <CDropdownToggle style={{ color: 'white' }}> | ||||||
|  |             <CIcon content={cilSettings} /> | ||||||
|  |           </CDropdownToggle> | ||||||
|  |           <CDropdownMenu placement="bottom-end"> | ||||||
|  |             <CDropdownItem>View Logs</CDropdownItem> | ||||||
|  |           </CDropdownMenu> | ||||||
|  |         </CDropdown> | ||||||
|  |       </CWidgetProgressIcon> | ||||||
|     ); |     ); | ||||||
| } |   } | ||||||
|  |  | ||||||
|  |   if (device) { | ||||||
|  |     return ( | ||||||
|  |       <CCard> | ||||||
|  |         <CCardHeader>Device #{device.serialNumber} Configuration</CCardHeader> | ||||||
|  |         <CCardBody> | ||||||
|  |           <CForm action="" method="post" encType="multipart/form-data" className="form-horizontal"> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>UUID : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.UUID} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Serial Number : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.serialNumber} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Device Type : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.deviceType} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>Last Configuration Change : </CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {cleanTimestamp(device.lastConfigurationChange)} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CFormGroup row> | ||||||
|  |               <CCol md="3"> | ||||||
|  |                 <CLabel>MAC address :</CLabel> | ||||||
|  |               </CCol> | ||||||
|  |               <CCol xs="12" md="9"> | ||||||
|  |                 {device.macAddress} | ||||||
|  |               </CCol> | ||||||
|  |             </CFormGroup> | ||||||
|  |             <CCollapse show={collapse}> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Created : </CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {cleanTimestamp(device.createdTimestamp)} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Last Configuration Download : </CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {cleanTimestamp(device.lastConfigurationDownload)} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Manufacturer :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.manufacturer} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel htmlFor="text-input">Notes :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   <CInput id="text-input" name="text-input" placeholder={device.notes} /> | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Owner :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.owner} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |               <CFormGroup row> | ||||||
|  |                 <CCol md="3"> | ||||||
|  |                   <CLabel>Location :</CLabel> | ||||||
|  |                 </CCol> | ||||||
|  |                 <CCol xs="12" md="9"> | ||||||
|  |                   {device.location} | ||||||
|  |                 </CCol> | ||||||
|  |               </CFormGroup> | ||||||
|  |             </CCollapse> | ||||||
|  |             <CCardFooter> | ||||||
|  |               <CButton color="primary" onClick={toggle} className="mb-1"> | ||||||
|  |                 More details | ||||||
|  |               </CButton> | ||||||
|  |             </CCardFooter> | ||||||
|  |           </CForm> | ||||||
|  |         </CCardBody> | ||||||
|  |       </CCard> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <CCard> | ||||||
|  |       <CCardHeader>Device Configuration</CCardHeader> | ||||||
|  |       <CCardBody /> | ||||||
|  |     </CCard> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
| export default DeviceStatus; | export default DeviceStatus; | ||||||
| @@ -1,209 +1,208 @@ | |||||||
| import { | import { | ||||||
| 	CButton, |   CButton, | ||||||
| 	CModal, |   CModal, | ||||||
| 	CModalHeader, |   CModalHeader, | ||||||
| 	CModalTitle, |   CModalTitle, | ||||||
| 	CModalBody, |   CModalBody, | ||||||
| 	CModalFooter, |   CModalFooter, | ||||||
| 	CSpinner, |   CSpinner, | ||||||
|     CBadge, |   CBadge, | ||||||
|     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'; | ||||||
|  |  | ||||||
| const FirmwareUpgradeModal = ({ show, toggleModal }) => { | const FirmwareUpgradeModal = ({ show, toggleModal }) => { | ||||||
| 	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 [firmware, setFirmware] = useState(''); |   const [firmware, setFirmware] = useState(''); | ||||||
|     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 date = convertDateToUtc(new Date()); | ||||||
|  |     if (date.getHours() >= 3) { | ||||||
|  |       date.setDate(date.getDate() + 1); | ||||||
|     } |     } | ||||||
|  |     date.setHours(3); | ||||||
|  |     date.setMinutes(0); | ||||||
|  |  | ||||||
|     const setDateToLate = () => { |     setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|         const date = convertDateToUtc(new Date()); |   }; | ||||||
|         if (date.getHours() >= 3){ |  | ||||||
|             date.setDate(date.getDate() + 1); |  | ||||||
|         } |  | ||||||
|         date.setHours(3); |  | ||||||
|         date.setMinutes(0); |  | ||||||
|  |  | ||||||
|         setChosenDate(convertDateFromUtc(date).toISOString()); |   const setDate = (date) => { | ||||||
|  |     if (date) { | ||||||
|  |       setChosenDate(date.toISOString()); | ||||||
|     } |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     const setDate = (date) => { |   const confirmingIfSure = () => { | ||||||
|         if(date){ |     setCheckingIfSure(true); | ||||||
|             setChosenDate(date.toISOString()); |   }; | ||||||
|         } |   useEffect(() => { | ||||||
|     } |     setHadSuccess(false); | ||||||
|  |     setHadFailure(false); | ||||||
|  |     setWaiting(false); | ||||||
|  |     setChosenDate(null); | ||||||
|  |     setFirmware(''); | ||||||
|  |     setValidFirmware(true); | ||||||
|  |     setResponseBody(''); | ||||||
|  |     setCheckingIfSure(false); | ||||||
|  |   }, [show]); | ||||||
|  |  | ||||||
|     const confirmingIfSure = () => { |   useEffect(() => { | ||||||
|         setCheckingIfSure(true); |     setValidFirmware(true); | ||||||
|     } |   }, [firmware]); | ||||||
|     useEffect(() => { |  | ||||||
|         setHadSuccess(false); |   const postUpgrade = () => { | ||||||
|         setHadFailure(false); |     setHadFailure(false); | ||||||
|         setWaiting(false); |     setHadSuccess(false); | ||||||
|         setChosenDate(null); |     setWaiting(true); | ||||||
|         setFirmware(''); |  | ||||||
|         setValidFirmware(true); |     const token = getToken(); | ||||||
|         setResponseBody(''); |  | ||||||
|  |     const headers = { | ||||||
|  |       Accept: 'application/json', | ||||||
|  |       Authorization: `Bearer ${token}`, | ||||||
|  |       serialNumber: selectedDeviceId, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const parameters = { | ||||||
|  |       serialNumber: selectedDeviceId, | ||||||
|  |       when: chosenDate, | ||||||
|  |       uri: firmware, | ||||||
|  |     }; | ||||||
|  |     axiosInstance | ||||||
|  |       .post(`/device/${selectedDeviceId}/upgrade`, parameters, { headers }) | ||||||
|  |       .then((response) => { | ||||||
|  |         setResponseBody(JSON.stringify(response.data, null, 4)); | ||||||
|  |         setHadSuccess(true); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         setHadFailure(true); | ||||||
|  |         console.log(error); | ||||||
|  |         console.log(error.response); | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|         setCheckingIfSure(false); |         setCheckingIfSure(false); | ||||||
|     },[show]); |         setWaiting(false); | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     useEffect(() => { |   return ( | ||||||
|         setValidFirmware(true); |     <CModal show={show} onClose={toggleModal}> | ||||||
|     },[firmware]); |       <CModalHeader closeButton> | ||||||
|  |         <CModalTitle>Firmware Upgrade</CModalTitle> | ||||||
|  |       </CModalHeader> | ||||||
|  |       <CModalBody> | ||||||
|  |         <h6>Choose a time and a firmware version for this device</h6> | ||||||
|  |         <CRow style={{ marginTop: '20px' }}> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block color="primary" onClick={() => setDateToNow()}> | ||||||
|  |               Now | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block color="primary" onClick={() => setDateToLate()}> | ||||||
|  |               Later tonight | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |         <CRow style={{ marginTop: '20px' }}> | ||||||
|  |           <CCol md="4" style={{ marginTop: '7px' }}> | ||||||
|  |             <p>Local time:</p> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol xs="12" md="8"> | ||||||
|  |             <DatePicker | ||||||
|  |               selected={Date.parse(chosenDate)} | ||||||
|  |               includeTime | ||||||
|  |               selectTime | ||||||
|  |               placeholder="Select custom date in UTC" | ||||||
|  |               disabled={waiting} | ||||||
|  |               onChange={(date) => setDate(date)} | ||||||
|  |               min={new Date()} | ||||||
|  |             /> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |  | ||||||
|     const postUpgrade = () => { |         <div style={{ marginTop: '25px' }}> | ||||||
|         setHadFailure(false); |           <p> | ||||||
|         setHadSuccess(false); |             Device will upgrade at (UTC): <b>{chosenDate}</b> | ||||||
|         setWaiting(true); |           </p> | ||||||
|  |         </div> | ||||||
|         const token = getToken(); |         <CInput | ||||||
|  |           disabled={waiting} | ||||||
| 		const headers = { |           className={('form-control', { 'is-invalid': !validFirmware })} | ||||||
| 			'Accept': 'application/json', |           type="text" | ||||||
| 			'Authorization': `Bearer ${token}`, |           id="uri" | ||||||
| 			'serialNumber': selectedDeviceId |           name="uri-input" | ||||||
| 		}; |           placeholder="https://somelocation.com/file=newversion.bin" | ||||||
|  |           autoComplete="firmware-uri" | ||||||
|         const parameters = { |           onChange={(event) => setFirmware(event.target.value)} | ||||||
|             serialNumber: selectedDeviceId, |           value={firmware} | ||||||
|             when: chosenDate, |         /> | ||||||
|             uri: firmware |         <CInvalidFeedback>You need a url...</CInvalidFeedback> | ||||||
|         } |         <div hidden={!hadSuccess && !hadFailure}> | ||||||
| 		axiosInstance.post(`/device/${selectedDeviceId}/upgrade`, parameters,{ headers: headers}) |           <div> | ||||||
| 		.then((response) => { |             <pre>{responseBody}</pre> | ||||||
|             setResponseBody(JSON.stringify(response.data, null, 4)); |           </div> | ||||||
| 			setHadSuccess(true); |         </div> | ||||||
| 		}) |       </CModalBody> | ||||||
| 		.catch(error => { |       <CModalFooter> | ||||||
| 			setHadFailure(true); |         <div hidden={!checkingIfSure}>Are you sure?</div> | ||||||
| 			console.log(error); |         <CButton | ||||||
| 			console.log(error.response); |           hidden={checkingIfSure} | ||||||
| 		}) |           color="primary" | ||||||
| 		.finally (() => { |           onClick={() => (formValidation() ? confirmingIfSure() : null)} | ||||||
|             setCheckingIfSure(false); |  | ||||||
| 			setWaiting(false); |  | ||||||
| 		}); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|         <CModal  |  | ||||||
|             show={show}  |  | ||||||
|             onClose={toggleModal} |  | ||||||
|         > |         > | ||||||
|             <CModalHeader closeButton> |           Schedule Upgrade | ||||||
|                 <CModalTitle>Firmware Upgrade</CModalTitle> |         </CButton> | ||||||
|             </CModalHeader> |         <CButton | ||||||
|             <CModalBody> |           hidden={!checkingIfSure} | ||||||
|                 <h6>Choose a time and a firmware version for this device</h6> |           disabled={waiting} | ||||||
|                 <CRow style={{marginTop: '20px'}}> |           color="primary" | ||||||
|                     <CCol> |           onClick={() => (formValidation() ? postUpgrade() : null)} | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton> |         > | ||||||
|                     </CCol> |           {waiting ? 'Loading...' : 'Yes'} {'   '} | ||||||
|                     <CCol> |           <CSpinner hidden={!waiting} component="span" size="sm" /> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>  |           <CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill"> | ||||||
|                     </CCol> |             Success | ||||||
|                 </CRow> |           </CBadge> | ||||||
|                 <CRow style={{marginTop: '20px'}}> |           <CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill"> | ||||||
|                     <CCol md="4" style={{marginTop: '7px'}}> |             Request Failed | ||||||
|                         <p>Local time:</p> |           </CBadge> | ||||||
|                     </CCol> |         </CButton> | ||||||
|                     <CCol xs="12" md="8"> |         <CButton color="secondary" onClick={toggleModal}> | ||||||
|                         <DatePicker |           Cancel | ||||||
|                             selected = { Date.parse(chosenDate) } |         </CButton> | ||||||
|                             includeTime  |       </CModalFooter> | ||||||
|                             selectTime |     </CModal> | ||||||
|                             placeholder = 'Select custom date in UTC' |   ); | ||||||
|                             disabled = {waiting} | }; | ||||||
|                             onChange = {date => setDate(date)}   |  | ||||||
|                             min = { new Date() } |  | ||||||
|                         /> |  | ||||||
|                     </CCol> |  | ||||||
|                 </CRow> |  | ||||||
|  |  | ||||||
|                 <div style={{marginTop : '25px'}}> |  | ||||||
|                     <p>Device will upgrade at (UTC): <b>{ chosenDate }</b></p> |  | ||||||
|                 </div> |  | ||||||
|                 <CInput  |  | ||||||
|                     disabled = { waiting } |  | ||||||
|                     className = {'form-control', {'is-invalid' : !validFirmware}}  |  | ||||||
|                     type = "text"  |  | ||||||
|                     id="uri"  |  | ||||||
|                     name="uri-input"  |  | ||||||
|                     placeholder="https://somelocation.com/file=newversion.bin"  |  | ||||||
|                     autoComplete="firmware-uri"  |  | ||||||
|                     onChange={event => setFirmware(event.target.value)} |  | ||||||
|                     value={firmware} |  | ||||||
|                 /> |  | ||||||
| 			    <CInvalidFeedback>You need a url...</CInvalidFeedback> |  | ||||||
|                 <div hidden={(!hadSuccess && !hadFailure)}> |  | ||||||
|                     <div><pre>{responseBody}</pre></div> |  | ||||||
|                 </div> |  | ||||||
|             </CModalBody> |  | ||||||
|             <CModalFooter> |  | ||||||
|                 <div hidden= { !checkingIfSure }> |  | ||||||
|                     Are you sure? |  | ||||||
|                 </div> |  | ||||||
|                 <CButton |  | ||||||
|                     hidden = { checkingIfSure }  |  | ||||||
|                     color="primary"  |  | ||||||
|                     onClick={event => formValidation() ? confirmingIfSure() : null} |  | ||||||
|                 > |  | ||||||
|                     Schedule Upgrade |  | ||||||
|                 </CButton> |  | ||||||
|                 <CButton |  | ||||||
|                     hidden = { !checkingIfSure }  |  | ||||||
|                     disabled={ waiting } |  | ||||||
|                     color="primary"  |  | ||||||
|                     onClick={event => formValidation() ? postUpgrade() : null} |  | ||||||
|                 > |  | ||||||
|                     { waiting ? 'Loading...' : 'Yes'} {'   '} |  | ||||||
|                     <CSpinner hidden={ !waiting } component="span" size="sm"/> |  | ||||||
|                     <CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill"> |  | ||||||
|                         Success |  | ||||||
|                     </CBadge> |  | ||||||
|                     <CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill"> |  | ||||||
|                         Request Failed |  | ||||||
|                     </CBadge> |  | ||||||
|                 </CButton> |  | ||||||
|                 <CButton  |  | ||||||
|                     color="secondary"  |  | ||||||
|                     onClick={ toggleModal } |  | ||||||
|                 > |  | ||||||
|                     Cancel |  | ||||||
|                 </CButton> |  | ||||||
|             </CModalFooter> |  | ||||||
|              |  | ||||||
| 		</CModal> |  | ||||||
|     ); |  | ||||||
|      |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default FirmwareUpgradeModal; | export default FirmwareUpgradeModal; | ||||||
| @@ -1,3 +1,3 @@ | |||||||
| { | { | ||||||
|     "REACT_APP_BASE_URL" : "https://ucentral.dpaas.arilia.com:16001/api/v1" |   "REACT_APP_BASE_URL": "https://ucentral.dpaas.arilia.com:16001/api/v1" | ||||||
| } | } | ||||||
| @@ -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( | ||||||
|             { |             (route) => | ||||||
|             routes.map((route, idx) => { |               route.component && ( | ||||||
|               return 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" /> |           )} | ||||||
|           </Switch> |           <Redirect from="/" to="/devices" /> | ||||||
|         </Suspense> |         </Switch> | ||||||
|       </CContainer> |       </Suspense> | ||||||
|     </main> |     </CContainer> | ||||||
|   ) |   </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"> | ||||||
|         <a href="https://coreui.io" target="_blank" rel="noopener noreferrer">CoreUI</a> |         CoreUI | ||||||
|         <span className="ml-1">© 2020 creativeLabs.</span> |       </a> | ||||||
|       </div> |       <span className="ml-1">© 2020 creativeLabs.</span> | ||||||
|       <div className="mfs-auto"> |     </div> | ||||||
|         <span className="mr-1">Powered by</span> |     <div className="mfs-auto"> | ||||||
|         <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer">CoreUI for React</a> |       <span className="mr-1">Powered by</span> | ||||||
|       </div> |       <a href="https://coreui.io/react" target="_blank" rel="noopener noreferrer"> | ||||||
|     </CFooter> |         CoreUI for React | ||||||
|   ) |       </a> | ||||||
| } |     </div> | ||||||
|  |   </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"  |         <div className="d-md-down-none mfe-2 c-subheader-nav"> | ||||||
|           routes={routes} |           <CLink className="c-subheader-nav-link" aria-current="page" to="/devices"> | ||||||
|         /> |             <CIcon name="cil-graph" alt="Dashboard" /> | ||||||
|           <div className="d-md-down-none mfe-2 c-subheader-nav"> |              Dashboard | ||||||
|             <CLink  |           </CLink> | ||||||
|               className="c-subheader-nav-link"  |           <CLink className="c-subheader-nav-link" href="#"> | ||||||
|               aria-current="page"  |             <CIcon name="cil-settings" alt="Settings" /> | ||||||
|               to="/devices" |              Settings | ||||||
|             > |           </CLink> | ||||||
|               <CIcon name="cil-graph" alt="Dashboard" /> Dashboard |         </div> | ||||||
|             </CLink> |  | ||||||
|             <CLink className="c-subheader-nav-link" href="#"> |  | ||||||
|               <CIcon name="cil-settings" alt="Settings" /> Settings |  | ||||||
|             </CLink> |  | ||||||
|           </div> |  | ||||||
|       </CSubheader> |       </CSubheader> | ||||||
|     </CHeader> |     </CHeader> | ||||||
|   ) |   ); | ||||||
| } | }; | ||||||
|  |  | ||||||
| export default TheHeader | export default TheHeader; | ||||||
|   | |||||||
| @@ -1,33 +1,27 @@ | |||||||
| 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"> | ||||||
|       <TheSidebar/> |       <TheSidebar /> | ||||||
|       <div className="c-wrapper"> |       <div className="c-wrapper"> | ||||||
|         <TheHeader/> |         <TheHeader /> | ||||||
|         <div className="c-body"> |         <div className="c-body"> | ||||||
|           <TheContent/> |           <TheContent /> | ||||||
|         </div> |         </div> | ||||||
|         <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; |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/index.js
									
									
									
									
									
								
							| @@ -1,19 +1,19 @@ | |||||||
| 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; | ||||||
|  |  | ||||||
| ReactDOM.render( | ReactDOM.render( | ||||||
|   <React.StrictMode> |   <React.StrictMode> | ||||||
|     <Provider store={store}> |     <Provider store={store}> | ||||||
|       <App/> |       <App /> | ||||||
|     </Provider> |     </Provider> | ||||||
|   </React.StrictMode>, |   </React.StrictMode>, | ||||||
|   document.getElementById('root') |   document.getElementById('root'), | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -4,10 +4,10 @@ const DevicePage = React.lazy(() => import('./views/pages/DevicePage')); | |||||||
| const DeviceListPage = React.lazy(() => import('./views/pages/DeviceListPage')); | const DeviceListPage = React.lazy(() => import('./views/pages/DeviceListPage')); | ||||||
|  |  | ||||||
| const routes = [ | const routes = [ | ||||||
|     { path: '/devices', exact: true, name: 'Devices', component: DeviceListPage }, |   { path: '/devices', exact: true, name: 'Devices', component: DeviceListPage }, | ||||||
|     { path: '/devices/:deviceId', name: 'Device Page', component: DevicePage }, |   { path: '/devices/:deviceId', name: 'Device Page', component: DevicePage }, | ||||||
|     { path: '/Device', name: 'Device', component: DevicePage }, |   { path: '/Device', name: 'Device', component: DevicePage }, | ||||||
|     { path: '/page2', name: 'Page2', component: DeviceListPage, exact: true }, |   { path: '/page2', name: 'Page2', component: DeviceListPage, exact: true }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|   export default routes; | export default routes; | ||||||
|   | |||||||
| @@ -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; | ||||||
| } | }; | ||||||
|   | |||||||
| @@ -3,45 +3,46 @@ import axiosRetry from 'axios-retry'; | |||||||
|  |  | ||||||
| const axiosInstance = axios.create(); | const axiosInstance = axios.create(); | ||||||
|  |  | ||||||
| axiosRetry(axiosInstance , { | axiosRetry(axiosInstance, { | ||||||
|     retries: 3, |   retries: 3, | ||||||
|     retryDelay: (retryCount) => { |   retryDelay: (retryCount) => { | ||||||
|         console.log(`retry attempt: ${retryCount}`); |     console.log(`retry attempt: ${retryCount}`); | ||||||
|         return axiosRetry.exponentialDelay; |     return axiosRetry.exponentialDelay; | ||||||
|     }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| 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 url = sessionStorage.getItem('gw_url'); |   const newConfig = config; | ||||||
|     if(url !== undefined && url !== null && !config.url.includes(url)){ |   const url = sessionStorage.getItem('gw_url'); | ||||||
|         config.url = url + config.url; |   if (url !== undefined && url !== null && !newConfig.url.includes(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,31 +1,31 @@ | |||||||
| 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']; | ||||||
|     if (!bytes || bytes === 0) { |   if (!bytes || bytes === 0) { | ||||||
|         return '0 B'; |     return '0 B'; | ||||||
|     } |   } | ||||||
|     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">{getIconsView(brandSet)}</CRow> | ||||||
|         <CRow className="text-center"> |     </CCardBody> | ||||||
|           {getIconsView(brandSet)} |   </CCard> | ||||||
|         </CRow> | ); | ||||||
|       </CCardBody> |  | ||||||
|     </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 <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub" /> | ||||||
|         Free Icons / as CIcon{' '} |     </CCardHeader> | ||||||
|         <DocsLink href="https://github.com/coreui/coreui-icons" text="GitHub"/> |     <CCardBody> | ||||||
|       </CCardHeader> |       <CRow className="text-center">{getIconsView(freeSet)}</CRow> | ||||||
|       <CCardBody> |     </CCardBody> | ||||||
|         <CRow className="text-center"> |   </CCard> | ||||||
|           {getIconsView(freeSet)} | ); | ||||||
|         </CRow> |  | ||||||
|       </CCardBody> |  | ||||||
|     </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">{getIconsView(flagSet)}</CRow> | ||||||
|         <CRow className="text-center"> |     </CCardBody> | ||||||
|           {getIconsView(flagSet)} |   </CCard> | ||||||
|         </CRow> | ); | ||||||
|       </CCardBody> |  | ||||||
|     </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,45 +1,42 @@ | |||||||
| 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 }); | ||||||
|         selectedDeviceId = deviceId; |     selectedDeviceId = deviceId; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|     useEffect(() => { |   useEffect(() => { | ||||||
|         dispatch({type: 'set', selectedDevice: null, selectedDeviceId: null}); |     dispatch({ type: 'set', selectedDevice: null, selectedDeviceId: null }); | ||||||
|     },[]); |   }, []); | ||||||
|  |  | ||||||
|     return ( |   return ( | ||||||
|         <> |     <> | ||||||
|         <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,147 +14,185 @@ 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 loginErrorText = | ||||||
|  |     'Login error, confirm that your username, password and gateway url are valid'; | ||||||
|  |  | ||||||
| 	const onKeyDown = (event) => { |   const formValidation = () => { | ||||||
| 	if(event.code === 'Enter' && formValidation()){ |     setHadError(false); | ||||||
| 		SignIn({ userId, password }); |  | ||||||
| 	} |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	const SignIn = (credentials) => { |     let isSuccesful = true; | ||||||
| 		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 = () => { |     if (userId.trim() === '') { | ||||||
| 		setError(false); |       setEmptyUsername(true); | ||||||
|  |       isSuccesful = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		let isSuccesful = true; |     if (password.trim() === '') { | ||||||
|  |       setEmptyPassword(true); | ||||||
|  |       isSuccesful = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
| 		if(userId.trim() === ''){ |     if (gatewayUrl.trim() === '') { | ||||||
| 			setEmptyUsername(true); |       setEmptyGateway(true); | ||||||
| 			isSuccesful = false; |       isSuccesful = false; | ||||||
| 		} |     } | ||||||
|  |  | ||||||
| 		if(password.trim() === ''){ |     return isSuccesful; | ||||||
| 			setEmptyPassword(true); |   }; | ||||||
| 			isSuccesful = false; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if(gatewayUrl.trim() === ''){ |   const SignIn = (credentials) => { | ||||||
| 			setEmptyGateway(true); |     axiosInstance | ||||||
| 			isSuccesful = false; |       .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); | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
| 		return isSuccesful; |   const onKeyDown = (event) => { | ||||||
| 	}; |     if (event.code === 'Enter' && formValidation()) { | ||||||
|  |       SignIn({ userId, password }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
| 	useEffect(() => { if (emptyUsername) setEmptyUsername(false); }, [userId]); |   useEffect(() => { | ||||||
| 	useEffect(() => { if (emptyPassword) setEmptyPassword(false); }, [password]); |     if (emptyUsername) setEmptyUsername(false); | ||||||
| 	useEffect(() => { if (emptyGateway) setEmptyGateway(false); }, [gatewayUrl]); |   }, [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"> | ||||||
| 			<CContainer> |       <CContainer> | ||||||
| 				<CRow className="justify-content-center"> |         <CRow className="justify-content-center"> | ||||||
| 					<CCol md="8"> |           <CCol md="8"> | ||||||
| 						<CCardGroup> |             <CCardGroup> | ||||||
| 							<CCard className="p-4"> |               <CCard className="p-4"> | ||||||
| 								<CCardBody> |                 <CCardBody> | ||||||
| 								<CForm onKeyDown = { onKeyDown }> |                   <CForm onKeyDown={onKeyDown}> | ||||||
| 									<h1>Login</h1> |                     <h1>Login</h1> | ||||||
| 									<p className="text-muted">Sign In to your account</p> |                     <p className="text-muted">Sign In to your account</p> | ||||||
| 									<CInputGroup className="mb-3"> |                     <CInputGroup className="mb-3"> | ||||||
| 										<CPopover content="Username"> |                       <CPopover content="Username"> | ||||||
| 											<CInputGroupPrepend> |                         <CInputGroupPrepend> | ||||||
| 											<CInputGroupText> |                           <CInputGroupText> | ||||||
| 												<CIcon name="cilUser" content={cilUser }/> |                             <CIcon name="cilUser" content={cilUser} /> | ||||||
| 											</CInputGroupText> |                           </CInputGroupText> | ||||||
| 											</CInputGroupPrepend> |                         </CInputGroupPrepend> | ||||||
| 										</CPopover> |                       </CPopover> | ||||||
| 										<CInput invalid = { emptyUsername } autoFocus required type="text" placeholder="Username" autoComplete="username" onChange={event => setUsername(event.target.value)}/> |                       <CInput | ||||||
| 										<CInvalidFeedback className="help-block"> |                         invalid={emptyUsername} | ||||||
| 											Please enter your username |                         autoFocus | ||||||
| 										</CInvalidFeedback> |                         required | ||||||
| 									</CInputGroup> |                         type="text" | ||||||
| 									<CInputGroup className="mb-4"> |                         placeholder="Username" | ||||||
| 										<CPopover content="Password"> |                         autoComplete="username" | ||||||
| 											<CInputGroupPrepend> |                         onChange={(event) => setUsername(event.target.value)} | ||||||
| 											<CInputGroupText> |                       /> | ||||||
| 												<CIcon name="cilLockLocked" content={ cilLockLocked }/> |                       <CInvalidFeedback className="help-block"> | ||||||
| 											</CInputGroupText> |                         Please enter your username | ||||||
| 											</CInputGroupPrepend> |                       </CInvalidFeedback> | ||||||
| 										</CPopover> |                     </CInputGroup> | ||||||
| 										<CInput invalid = { emptyPassword } type="password" required placeholder="Password" autoComplete="current-password" onChange={event => setPassword(event.target.value)} /> |                     <CInputGroup className="mb-4"> | ||||||
| 										<CInvalidFeedback className="help-block"> |                       <CPopover content="Password"> | ||||||
| 											Please enter your password |                         <CInputGroupPrepend> | ||||||
| 										</CInvalidFeedback> |                           <CInputGroupText> | ||||||
| 									</CInputGroup> |                             <CIcon content={cilLockLocked} /> | ||||||
| 									<CInputGroup className="mb-4"> |                           </CInputGroupText> | ||||||
| 										<CPopover content="Gateway URL"> |                         </CInputGroupPrepend> | ||||||
| 										<CInputGroupPrepend> |                       </CPopover> | ||||||
| 											<CInputGroupText> |                       <CInput | ||||||
| 											<CIcon name="cilLink" content={ cilLink }/> |                         invalid={emptyPassword} | ||||||
| 											</CInputGroupText> |                         required | ||||||
| 										</CInputGroupPrepend> |                         type="password" | ||||||
| 										</CPopover> |                         placeholder="Password" | ||||||
| 										<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)} /> |                         autoComplete="current-password" | ||||||
| 										<CInvalidFeedback className="help-block"> |                         onChange={(event) => setPassword(event.target.value)} | ||||||
| 											Please enter a gateway url |                       /> | ||||||
| 										</CInvalidFeedback> |                       <CInvalidFeedback className="help-block"> | ||||||
| 									</CInputGroup> |                         Please enter your password | ||||||
| 									<CRow> |                       </CInvalidFeedback> | ||||||
| 										<CCol> |                     </CInputGroup> | ||||||
| 										<CAlert show = { error } color="danger"> |                     <CInputGroup className="mb-4"> | ||||||
| 											Login error, confirm that your username, password and gateway url are valid |                       <CPopover content="Gateway URL"> | ||||||
| 										</CAlert> |                         <CInputGroupPrepend> | ||||||
| 										</CCol> |                           <CInputGroupText> | ||||||
| 									</CRow> |                             <CIcon name="cilLink" content={cilLink} /> | ||||||
| 									<CRow> |                           </CInputGroupText> | ||||||
| 									<CCol xs="6"> |                         </CInputGroupPrepend> | ||||||
| 										<CButton color="primary" className="px-4" onClick={event => formValidation() ? SignIn({ userId, password }) : null }>Login |                       </CPopover> | ||||||
| 									</CButton> |                       <CInput | ||||||
| 									</CCol> |                         invalid={emptyGateway} | ||||||
| 									<CCol xs="6" className="text-right"> |                         type="text" | ||||||
| 										<CButton color="link" className="px-0">Forgot password?</CButton> |                         required | ||||||
| 									</CCol> |                         placeholder={placeholderUrl} | ||||||
| 									</CRow> |                         value={gatewayUrl} | ||||||
| 								</CForm> |                         autoComplete="gateway-url" | ||||||
| 								</CCardBody> |                         onChange={(event) => setGatewayUrl(event.target.value)} | ||||||
| 							</CCard> |                       /> | ||||||
| 						</CCardGroup> |                       <CInvalidFeedback className="help-block"> | ||||||
| 					</CCol> |                         Please enter a gateway url | ||||||
| 				</CRow> |                       </CInvalidFeedback> | ||||||
| 			</CContainer> |                     </CInputGroup> | ||||||
| 		</div> |                     <CRow> | ||||||
| 	) |                       <CCol> | ||||||
| } |                         <CAlert show={hadError} color="danger"> | ||||||
|    |                           {loginErrorText} | ||||||
|   export default Login |                         </CAlert> | ||||||
|  |                       </CCol> | ||||||
|  |                     </CRow> | ||||||
|  |                     <CRow> | ||||||
|  |                       <CCol xs="6"> | ||||||
|  |                         <CButton | ||||||
|  |                           color="primary" | ||||||
|  |                           className="px-4" | ||||||
|  |                           onClick={() => (formValidation() ? SignIn({ userId, password }) : null)} | ||||||
|  |                         > | ||||||
|  |                           Login | ||||||
|  |                         </CButton> | ||||||
|  |                       </CCol> | ||||||
|  |                       <CCol xs="6" className="text-right"> | ||||||
|  |                         <CButton color="link" className="px-0"> | ||||||
|  |                           Forgot password? | ||||||
|  |                         </CButton> | ||||||
|  |                       </CCol> | ||||||
|  |                     </CRow> | ||||||
|  |                   </CForm> | ||||||
|  |                 </CCardBody> | ||||||
|  |               </CCard> | ||||||
|  |             </CCardGroup> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |       </CContainer> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default Login; | ||||||
|   | |||||||
| @@ -1,168 +1,176 @@ | |||||||
| import { | import { | ||||||
| 	CButton, |   CButton, | ||||||
| 	CModal, |   CModal, | ||||||
| 	CModalHeader, |   CModalHeader, | ||||||
| 	CModalTitle, |   CModalTitle, | ||||||
| 	CModalBody, |   CModalBody, | ||||||
| 	CModalFooter, |   CModalFooter, | ||||||
| 	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 = ({ | ||||||
| 	const [hadSuccess, setHadSuccess] = useState(false); |   show, | ||||||
| 	const [hadFailure, setHadFailure] = useState(false); |   toggleModal, | ||||||
|     const [waiting, setWaiting] = useState(false); |   title, | ||||||
| 	const [chosenDate, setChosenDate] = useState(null); |   directions, | ||||||
|     const [responseBody, setResponseBody] = useState(''); |   actionLabel, | ||||||
|     const selectedDeviceId = useSelector(state => state.selectedDeviceId); |   action, | ||||||
|  |   extraParameters, | ||||||
|  | }) => { | ||||||
|  |   const [hadSuccess, setHadSuccess] = useState(false); | ||||||
|  |   const [hadFailure, setHadFailure] = useState(false); | ||||||
|  |   const [waiting, setWaiting] = useState(false); | ||||||
|  |   const [chosenDate, setChosenDate] = useState(null); | ||||||
|  |   const [responseBody, setResponseBody] = useState(''); | ||||||
|  |   const selectedDeviceId = useSelector((state) => state.selectedDeviceId); | ||||||
|  |  | ||||||
|     const formValidation = () => { |   const formValidation = () => chosenDate !== null; | ||||||
|         return (chosenDate !== null); |  | ||||||
|  |   const setDateToNow = () => { | ||||||
|  |     const now = new Date().toISOString(); | ||||||
|  |     setChosenDate(now); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const setDateToLate = () => { | ||||||
|  |     const date = convertDateToUtc(new Date()); | ||||||
|  |     if (date.getHours() >= 3) { | ||||||
|  |       date.setDate(date.getDate() + 1); | ||||||
|     } |     } | ||||||
|  |     date.setHours(3); | ||||||
|  |     date.setMinutes(0); | ||||||
|  |  | ||||||
|     const setDateToNow = () => { |     setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|         const now = (new Date()).toISOString(); |   }; | ||||||
|         setChosenDate(now); |  | ||||||
|  |   const setDate = (date) => { | ||||||
|  |     if (date) { | ||||||
|  |       setChosenDate(convertDateFromUtc(date).toISOString()); | ||||||
|     } |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|     const setDateToLate = () => { |   useEffect(() => { | ||||||
|         const date = convertDateToUtc(new Date()); |     setHadSuccess(false); | ||||||
|         if (date.getHours() >= 3){ |     setHadFailure(false); | ||||||
|             date.setDate(date.getDate() + 1); |     setWaiting(false); | ||||||
|         } |     setChosenDate(false); | ||||||
|         date.setHours(3); |     setResponseBody(''); | ||||||
|         date.setMinutes(0); |   }, [show]); | ||||||
|  |  | ||||||
|         setChosenDate(convertDateFromUtc(date).toISOString()); |   const doAction = () => { | ||||||
|     } |     setHadFailure(false); | ||||||
|  |     setHadSuccess(false); | ||||||
|  |     setWaiting(true); | ||||||
|  |  | ||||||
|     const setDate = (date) => { |     const token = getToken(); | ||||||
|         if(date){ |  | ||||||
|             setChosenDate(convertDateFromUtc(date).toISOString()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     useEffect(() => { |     const headers = { | ||||||
|         setHadSuccess(false); |       Accept: 'application/json', | ||||||
|         setHadFailure(false); |       Authorization: `Bearer ${token}`, | ||||||
|  |       serialNumber: selectedDeviceId, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const necessaryParameters = { | ||||||
|  |       serialNumber: selectedDeviceId, | ||||||
|  |       when: chosenDate, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const parameters = { ...necessaryParameters, ...extraParameters }; | ||||||
|  |  | ||||||
|  |     axiosInstance | ||||||
|  |       .post(`/device/${selectedDeviceId}/${action}`, parameters, { headers }) | ||||||
|  |       .then((response) => { | ||||||
|  |         setResponseBody(JSON.stringify(response.data, null, 4)); | ||||||
|  |         setHadSuccess(true); | ||||||
|  |       }) | ||||||
|  |       .catch((error) => { | ||||||
|  |         setHadFailure(true); | ||||||
|  |         console.log(error); | ||||||
|  |         console.log(error.response); | ||||||
|  |       }) | ||||||
|  |       .finally(() => { | ||||||
|         setWaiting(false); |         setWaiting(false); | ||||||
|         setChosenDate(false); |       }); | ||||||
|         setResponseBody(''); |   }; | ||||||
|     },[show]); |  | ||||||
|  |  | ||||||
|     const doAction = () => { |   return ( | ||||||
|         setHadFailure(false); |     <CModal show={show} onClose={toggleModal}> | ||||||
|         setHadSuccess(false); |       <CModalHeader closeButton> | ||||||
|         setWaiting(true); |         <CModalTitle>{title}</CModalTitle> | ||||||
|  |       </CModalHeader> | ||||||
|  |       <CModalBody> | ||||||
|  |         <h6>{directions}</h6> | ||||||
|  |         <CRow style={{ marginTop: '20px' }}> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block color="primary" onClick={() => setDateToNow()}> | ||||||
|  |               Now | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol> | ||||||
|  |             <CButton block color="primary" onClick={() => setDateToLate()}> | ||||||
|  |               Later tonight | ||||||
|  |             </CButton> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |         <CRow style={{ marginTop: '20px' }}> | ||||||
|  |           <CCol md="4" style={{ marginTop: '7px' }}> | ||||||
|  |             <p>Custom (UTC):</p> | ||||||
|  |           </CCol> | ||||||
|  |           <CCol xs="12" md="8"> | ||||||
|  |             <DatePicker | ||||||
|  |               selected={Date.parse(chosenDate)} | ||||||
|  |               includeTime | ||||||
|  |               selectTime | ||||||
|  |               placeholder="Select custom date in UTC" | ||||||
|  |               disabled={waiting} | ||||||
|  |               onChange={(date) => setDate(date)} | ||||||
|  |               min={convertDateToUtc(new Date())} | ||||||
|  |             /> | ||||||
|  |           </CCol> | ||||||
|  |         </CRow> | ||||||
|  |  | ||||||
|         const token = getToken(); |         <div style={{ marginTop: '25px' }}> | ||||||
|  |           <p> | ||||||
|  |             Device will {actionLabel} at (UTC): <b>{chosenDate}</b> | ||||||
|  |           </p> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
| 		const headers = { |         <div hidden={!hadSuccess && !hadFailure}> | ||||||
| 			'Accept': 'application/json', |           <div> | ||||||
| 			'Authorization': `Bearer ${token}`, |             <pre>{responseBody}</pre> | ||||||
| 			'serialNumber': selectedDeviceId |           </div> | ||||||
| 		}; |         </div> | ||||||
|  |       </CModalBody> | ||||||
|         const necessaryParameters = { |       <CModalFooter> | ||||||
|             serialNumber : selectedDeviceId, |         <CButton | ||||||
|             when: chosenDate |           disabled={waiting} | ||||||
|         } |           color="primary" | ||||||
|  |           onClick={() => (formValidation() ? doAction() : null)} | ||||||
|         const parameters = {...necessaryParameters, ...extraParameters }; |  | ||||||
|  |  | ||||||
| 		axiosInstance.post(`/device/${selectedDeviceId}/${action}`, parameters,{ headers: headers}) |  | ||||||
| 		.then((response) => { |  | ||||||
|             setResponseBody(JSON.stringify(response.data, null, 4)); |  | ||||||
| 			setHadSuccess(true); |  | ||||||
| 		}) |  | ||||||
| 		.catch(error => { |  | ||||||
| 			setHadFailure(true); |  | ||||||
| 			console.log(error); |  | ||||||
| 			console.log(error.response); |  | ||||||
| 		}) |  | ||||||
| 		.finally (() => { |  | ||||||
| 			setWaiting(false); |  | ||||||
| 		}); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return ( |  | ||||||
|         <CModal  |  | ||||||
|             show={show}  |  | ||||||
|             onClose={toggleModal} |  | ||||||
|         > |         > | ||||||
|             <CModalHeader closeButton> |           {waiting ? 'Loading...' : 'Schedule'} {'   '} | ||||||
|                 <CModalTitle>{ title }</CModalTitle> |           <CSpinner hidden={!waiting} component="span" size="sm" /> | ||||||
|             </CModalHeader> |           <CBadge hidden={waiting || !hadSuccess} color="success" shape="rounded-pill"> | ||||||
|             <CModalBody> |             Success | ||||||
|                 <h6>{ directions }</h6> |           </CBadge> | ||||||
|                 <CRow style={{marginTop: '20px'}}> |           <CBadge hidden={waiting || !hadFailure} color="danger" shape="rounded-pill"> | ||||||
|                     <CCol> |             Request Failed | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToNow() } >Now</CButton> |           </CBadge> | ||||||
|                     </CCol> |         </CButton> | ||||||
|                     <CCol> |         <CButton color="secondary" onClick={toggleModal}> | ||||||
|                         <CButton block color="primary" onClick={ () => setDateToLate() }>Later tonight</CButton>  |           Cancel | ||||||
|                     </CCol> |         </CButton> | ||||||
|                 </CRow> |       </CModalFooter> | ||||||
|                 <CRow style={{marginTop: '20px'}}> |     </CModal> | ||||||
|                     <CCol md="4" style={{marginTop: '7px'}}> |   ); | ||||||
|                         <p>Custom (UTC):</p> | }; | ||||||
|                     </CCol> |  | ||||||
|                     <CCol xs="12" md="8"> |  | ||||||
|                         <DatePicker |  | ||||||
|                             selected = { Date.parse(chosenDate) } |  | ||||||
|                             includeTime  |  | ||||||
|                             selectTime |  | ||||||
|                             placeholder = 'Select custom date in UTC' |  | ||||||
|                             disabled = {waiting} |  | ||||||
|                             onChange = {date => setDate(date)}   |  | ||||||
|                             min = { convertDateToUtc(new Date()) } |  | ||||||
|                         /> |  | ||||||
|                     </CCol> |  | ||||||
|                 </CRow> |  | ||||||
|  |  | ||||||
|                 <div style={{marginTop : '25px'}}> |  | ||||||
|                     <p>Device will { actionLabel } at (UTC): <b>{ chosenDate }</b></p> |  | ||||||
|                 </div> |  | ||||||
|  |  | ||||||
|                 <div hidden={(!hadSuccess && !hadFailure)}> |  | ||||||
|                 <div><pre>{responseBody}</pre></div> |  | ||||||
|                 </div> |  | ||||||
|             </CModalBody> |  | ||||||
|             <CModalFooter> |  | ||||||
|                 <CButton  |  | ||||||
|                     disabled={waiting} |  | ||||||
|                     color="primary"  |  | ||||||
|                     onClick={event => formValidation() ? doAction() : null} |  | ||||||
|                 > |  | ||||||
|                     { waiting ? 'Loading...' : 'Schedule'} {'   '} |  | ||||||
| 							<CSpinner hidden={ !waiting } component="span" size="sm"/> |  | ||||||
| 							<CBadge hidden = { (waiting || !hadSuccess) } color="success" shape="rounded-pill"> |  | ||||||
| 								Success |  | ||||||
| 							</CBadge> |  | ||||||
| 							<CBadge hidden = { (waiting || !hadFailure) }color="danger" shape="rounded-pill"> |  | ||||||
| 								Request Failed |  | ||||||
| 							</CBadge> |  | ||||||
|                 </CButton> |  | ||||||
|                 <CButton  |  | ||||||
|                     color="secondary"  |  | ||||||
|                     onClick={ toggleModal } |  | ||||||
|                 > |  | ||||||
|                     Cancel |  | ||||||
|                 </CButton> |  | ||||||
|             </CModalFooter> |  | ||||||
| 		</CModal> |  | ||||||
|     ); |  | ||||||
|      |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default ActionModalWidget; | export default ActionModalWidget; | ||||||
		Reference in New Issue
	
	Block a user
	 bourquecharles
					bourquecharles