add eslint airbnb rules

main
Cameron Clough 2022-01-09 16:52:29 +00:00
parent 0956687fbb
commit d70e46583a
No known key found for this signature in database
GPG Key ID: BFB3B74B026ED43F
28 changed files with 897 additions and 894 deletions

View File

@ -1,9 +1,39 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": [
"react-app",
"react-app/jest"
"react-app/jest",
"airbnb",
"airbnb/hooks"
],
"rules": {
"no-trailing-spaces": "warn"
// disallow use of unary operators, ++ and --
// http://eslint.org/docs/rules/no-plusplus
// retropilot: we allow them in the for loop
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
// disallow use of the continue statement
// https://eslint.org/docs/rules/no-continue
// retropilot: we allow use of the continue statement
"no-continue": "off",
// disallow use of variables before they are defined
// http://eslint.org/docs/rules/no-use-before-define
// retropilot: permit referencing functions before they're defined
"no-use-before-define": ["error", { "functions": false }],
// specify the maximum length of a line in your program
// https://eslint.org/docs/rules/max-len
// retropilot: ignore comments
"max-len": ["error", 100, 2, {
"ignoreUrls": true,
"ignoreComments": true,
"ignoreRegExpLiterals": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}]
}
}

288
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "app",
"name": "retropilot-client",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "app",
"name": "retropilot-client",
"version": "0.1.0",
"dependencies": {
"@emotion/react": "^11.7.1",
@ -30,6 +30,9 @@
"react-scripts": "4.0.3",
"styled-components": "^5.3.3",
"web-vitals": "^1.1.2"
},
"devDependencies": {
"eslint-config-airbnb": "^19.0.4"
}
},
"node_modules/@babel/code-frame": {
@ -5256,9 +5259,9 @@
"integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA=="
},
"node_modules/axe-core": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz",
"integrity": "sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA==",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz",
"integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==",
"engines": {
"node": ">=4"
}
@ -8374,6 +8377,55 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-config-airbnb": {
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
"integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
"dev": true,
"dependencies": {
"eslint-config-airbnb-base": "^15.0.0",
"object.assign": "^4.1.2",
"object.entries": "^1.1.5"
},
"engines": {
"node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^7.32.0 || ^8.2.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0"
}
},
"node_modules/eslint-config-airbnb-base": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
"integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
"dev": true,
"dependencies": {
"confusing-browser-globals": "^1.0.10",
"object.assign": "^4.1.2",
"object.entries": "^1.1.5",
"semver": "^6.3.0"
},
"engines": {
"node": "^10.12.0 || >=12.0.0"
},
"peerDependencies": {
"eslint": "^7.32.0 || ^8.2.0",
"eslint-plugin-import": "^2.25.2"
}
},
"node_modules/eslint-config-airbnb-base/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-config-react-app": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
@ -8436,13 +8488,12 @@
}
},
"node_modules/eslint-module-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz",
"integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==",
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz",
"integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==",
"dependencies": {
"debug": "^3.2.7",
"find-up": "^2.1.0",
"pkg-dir": "^2.0.0"
"find-up": "^2.1.0"
},
"engines": {
"node": ">=4"
@ -8517,17 +8568,6 @@
"node": ">=4"
}
},
"node_modules/eslint-module-utils/node_modules/pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"dependencies": {
"find-up": "^2.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/eslint-plugin-flowtype": {
"version": "5.10.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.10.0.tgz",
@ -8544,23 +8584,23 @@
}
},
"node_modules/eslint-plugin-import": {
"version": "2.25.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz",
"integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==",
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz",
"integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==",
"dependencies": {
"array-includes": "^3.1.4",
"array.prototype.flat": "^1.2.5",
"debug": "^2.6.9",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-module-utils": "^2.7.0",
"eslint-module-utils": "^2.7.2",
"has": "^1.0.3",
"is-core-module": "^2.7.0",
"is-core-module": "^2.8.0",
"is-glob": "^4.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.5",
"resolve": "^1.20.0",
"tsconfig-paths": "^3.11.0"
"tsconfig-paths": "^3.12.0"
},
"engines": {
"node": ">=4"
@ -8626,27 +8666,28 @@
}
},
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz",
"integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==",
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz",
"integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@babel/runtime": "^7.16.3",
"aria-query": "^4.2.2",
"array-includes": "^3.1.1",
"array-includes": "^3.1.4",
"ast-types-flow": "^0.0.7",
"axe-core": "^4.0.2",
"axe-core": "^4.3.5",
"axobject-query": "^2.2.0",
"damerau-levenshtein": "^1.0.6",
"emoji-regex": "^9.0.0",
"damerau-levenshtein": "^1.0.7",
"emoji-regex": "^9.2.2",
"has": "^1.0.3",
"jsx-ast-utils": "^3.1.0",
"language-tags": "^1.0.5"
"jsx-ast-utils": "^3.2.1",
"language-tags": "^1.0.5",
"minimatch": "^3.0.4"
},
"engines": {
"node": ">=4.0"
},
"peerDependencies": {
"eslint": "^3 || ^4 || ^5 || ^6 || ^7"
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": {
@ -8655,41 +8696,41 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"node_modules/eslint-plugin-react": {
"version": "7.26.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.26.1.tgz",
"integrity": "sha512-Lug0+NOFXeOE+ORZ5pbsh6mSKjBKXDXItUD2sQoT+5Yl0eoT82DqnXeTMfUare4QVCn9QwXbfzO/dBLjLXwVjQ==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
"integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==",
"dependencies": {
"array-includes": "^3.1.3",
"array.prototype.flatmap": "^1.2.4",
"array-includes": "^3.1.4",
"array.prototype.flatmap": "^1.2.5",
"doctrine": "^2.1.0",
"estraverse": "^5.2.0",
"estraverse": "^5.3.0",
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.0.4",
"object.entries": "^1.1.4",
"object.fromentries": "^2.0.4",
"object.hasown": "^1.0.0",
"object.values": "^1.1.4",
"object.entries": "^1.1.5",
"object.fromentries": "^2.0.5",
"object.hasown": "^1.1.0",
"object.values": "^1.1.5",
"prop-types": "^15.7.2",
"resolve": "^2.0.0-next.3",
"semver": "^6.3.0",
"string.prototype.matchall": "^4.0.5"
"string.prototype.matchall": "^4.0.6"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
"eslint": "^3 || ^4 || ^5 || ^6 || ^7"
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/eslint-plugin-react-hooks": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz",
"integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz",
"integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
}
},
"node_modules/eslint-plugin-react/node_modules/doctrine": {
@ -8704,9 +8745,9 @@
}
},
"node_modules/eslint-plugin-react/node_modules/estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"engines": {
"node": ">=4.0"
}
@ -19939,9 +19980,9 @@
}
},
"node_modules/tsconfig-paths": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz",
"integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==",
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
"integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
"dependencies": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
@ -25990,9 +26031,9 @@
}
},
"axe-core": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz",
"integrity": "sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA=="
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz",
"integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA=="
},
"axios": {
"version": "0.24.0",
@ -28635,6 +28676,37 @@
}
}
},
"eslint-config-airbnb": {
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
"integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
"dev": true,
"requires": {
"eslint-config-airbnb-base": "^15.0.0",
"object.assign": "^4.1.2",
"object.entries": "^1.1.5"
}
},
"eslint-config-airbnb-base": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
"integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
"dev": true,
"requires": {
"confusing-browser-globals": "^1.0.10",
"object.assign": "^4.1.2",
"object.entries": "^1.1.5",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"eslint-config-react-app": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
@ -28672,13 +28744,12 @@
}
},
"eslint-module-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz",
"integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==",
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz",
"integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==",
"requires": {
"debug": "^3.2.7",
"find-up": "^2.1.0",
"pkg-dir": "^2.0.0"
"find-up": "^2.1.0"
},
"dependencies": {
"debug": {
@ -28731,14 +28802,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"requires": {
"find-up": "^2.1.0"
}
}
}
},
@ -28752,23 +28815,23 @@
}
},
"eslint-plugin-import": {
"version": "2.25.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz",
"integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==",
"version": "2.25.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz",
"integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==",
"requires": {
"array-includes": "^3.1.4",
"array.prototype.flat": "^1.2.5",
"debug": "^2.6.9",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-module-utils": "^2.7.0",
"eslint-module-utils": "^2.7.2",
"has": "^1.0.3",
"is-core-module": "^2.7.0",
"is-core-module": "^2.8.0",
"is-glob": "^4.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.5",
"resolve": "^1.20.0",
"tsconfig-paths": "^3.11.0"
"tsconfig-paths": "^3.12.0"
},
"dependencies": {
"debug": {
@ -28812,21 +28875,22 @@
}
},
"eslint-plugin-jsx-a11y": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz",
"integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==",
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz",
"integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==",
"requires": {
"@babel/runtime": "^7.11.2",
"@babel/runtime": "^7.16.3",
"aria-query": "^4.2.2",
"array-includes": "^3.1.1",
"array-includes": "^3.1.4",
"ast-types-flow": "^0.0.7",
"axe-core": "^4.0.2",
"axe-core": "^4.3.5",
"axobject-query": "^2.2.0",
"damerau-levenshtein": "^1.0.6",
"emoji-regex": "^9.0.0",
"damerau-levenshtein": "^1.0.7",
"emoji-regex": "^9.2.2",
"has": "^1.0.3",
"jsx-ast-utils": "^3.1.0",
"language-tags": "^1.0.5"
"jsx-ast-utils": "^3.2.1",
"language-tags": "^1.0.5",
"minimatch": "^3.0.4"
},
"dependencies": {
"emoji-regex": {
@ -28837,24 +28901,24 @@
}
},
"eslint-plugin-react": {
"version": "7.26.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.26.1.tgz",
"integrity": "sha512-Lug0+NOFXeOE+ORZ5pbsh6mSKjBKXDXItUD2sQoT+5Yl0eoT82DqnXeTMfUare4QVCn9QwXbfzO/dBLjLXwVjQ==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz",
"integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==",
"requires": {
"array-includes": "^3.1.3",
"array.prototype.flatmap": "^1.2.4",
"array-includes": "^3.1.4",
"array.prototype.flatmap": "^1.2.5",
"doctrine": "^2.1.0",
"estraverse": "^5.2.0",
"estraverse": "^5.3.0",
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
"minimatch": "^3.0.4",
"object.entries": "^1.1.4",
"object.fromentries": "^2.0.4",
"object.hasown": "^1.0.0",
"object.values": "^1.1.4",
"object.entries": "^1.1.5",
"object.fromentries": "^2.0.5",
"object.hasown": "^1.1.0",
"object.values": "^1.1.5",
"prop-types": "^15.7.2",
"resolve": "^2.0.0-next.3",
"semver": "^6.3.0",
"string.prototype.matchall": "^4.0.5"
"string.prototype.matchall": "^4.0.6"
},
"dependencies": {
"doctrine": {
@ -28866,9 +28930,9 @@
}
},
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ=="
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
},
"resolve": {
"version": "2.0.0-next.3",
@ -28887,9 +28951,9 @@
}
},
"eslint-plugin-react-hooks": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz",
"integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz",
"integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==",
"requires": {}
},
"eslint-plugin-testing-library": {
@ -37426,9 +37490,9 @@
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw=="
},
"tsconfig-paths": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz",
"integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==",
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
"integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",

View File

@ -6,7 +6,9 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"lint": "eslint src --ext .js,.jsx",
"lint:fix": "npm run lint -- --fix"
},
"dependencies": {
"@emotion/react": "^11.7.1",
@ -32,6 +34,9 @@
"styled-components": "^5.3.3",
"web-vitals": "^1.1.2"
},
"devDependencies": {
"eslint-config-airbnb": "^19.0.4"
},
"browserslist": {
"production": [
">0.2%",

View File

@ -1,42 +1,29 @@
import CssBaseline from '@mui/material/CssBaseline';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import React, { useState } from 'react';
import Login from "./components/views/login";
import UserAdmin from "./components/views/useradmin";
import Login from './components/views/login';
import UserAdmin from './components/views/useradmin';
import GlobalSnack from './components/widgets/globalSnack';
import DeviceStore from "./context/devices";
import ToastStore from "./context/toast";
import { UserProvider } from "./context/users";
import * as authenticationController from "./controllers/authentication";
import DeviceStore from './context/devices';
import ToastStore from './context/toast';
import { UserProvider } from './context/users';
import * as authenticationController from './controllers/authentication';
// Connection opened
function App() {
const [session, setSession] = useState(false)
const [session, setSession] = useState(false);
authenticationController.getSession().then((res) => {
setSession(res.data.authenticated)
})
setSession(res.data.authenticated);
});
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
},
}),
() => createTheme({
palette: {
mode: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
},
}),
[],
);

View File

@ -2,5 +2,5 @@ import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<p></p>);
render(<p />);
});

View File

@ -6,123 +6,119 @@ import DrivesTable from './tabPane';
function DeviceLastSeenMap() {
return (
<div style={{ height: "500px", width: 'calc(100%)' }}>
<div style={{ height: '500px', width: 'calc(100%)' }}>
<GoogleMapReact
height="100px"
bootstrapURLKeys={{ key: process.env.REACT_APP_GMAPS_API_KEY }}
defaultCenter={{
lat: 51.501134,
lng: -0.142318
}}
defaultZoom={17}
options={{
styles: [
{ elementType: "geometry", stylers: [{ color: "#242f3e" }] },
{ elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
{ elementType: "labels.text.fill", stylers: [{ color: "#746855" }] },
{
featureType: "administrative.locality",
elementType: "labels.text.fill",
stylers: [{ color: "#d59563" }],
},
{
featureType: "poi",
elementType: "labels.text.fill",
stylers: [{ color: "#d59563" }],
},
{
featureType: "poi.park",
elementType: "geometry",
stylers: [{ color: "#263c3f" }],
},
{
featureType: "poi.park",
elementType: "labels.text.fill",
stylers: [{ color: "#6b9a76" }],
},
{
featureType: "road",
elementType: "geometry",
stylers: [{ color: "#38414e" }],
},
{
featureType: "road",
elementType: "geometry.stroke",
stylers: [{ color: "#212a37" }],
},
{
featureType: "road",
elementType: "labels.text.fill",
stylers: [{ color: "#9ca5b3" }],
},
{
featureType: "road.highway",
elementType: "geometry",
stylers: [{ color: "#746855" }],
},
{
featureType: "road.highway",
elementType: "geometry.stroke",
stylers: [{ color: "#1f2835" }],
},
{
featureType: "road.highway",
elementType: "labels.text.fill",
stylers: [{ color: "#f3d19c" }],
},
{
featureType: "transit",
elementType: "geometry",
stylers: [{ color: "#2f3948" }],
},
{
featureType: "transit.station",
elementType: "labels.text.fill",
stylers: [{ color: "#d59563" }],
},
{
featureType: "water",
elementType: "geometry",
stylers: [{ color: "#17263c" }],
},
{
featureType: "water",
elementType: "labels.text.fill",
stylers: [{ color: "#515c6d" }],
},
{
featureType: "water",
elementType: "labels.text.stroke",
stylers: [{ color: "#17263c" }],
},
],
}}
/>
</div>
)
<GoogleMapReact
height="100px"
bootstrapURLKeys={{ key: process.env.REACT_APP_GMAPS_API_KEY }}
defaultCenter={{
lat: 51.501134,
lng: -0.142318,
}}
defaultZoom={17}
options={{
styles: [
{ elementType: 'geometry', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.stroke', stylers: [{ color: '#242f3e' }] },
{ elementType: 'labels.text.fill', stylers: [{ color: '#746855' }] },
{
featureType: 'administrative.locality',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }],
},
{
featureType: 'poi',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }],
},
{
featureType: 'poi.park',
elementType: 'geometry',
stylers: [{ color: '#263c3f' }],
},
{
featureType: 'poi.park',
elementType: 'labels.text.fill',
stylers: [{ color: '#6b9a76' }],
},
{
featureType: 'road',
elementType: 'geometry',
stylers: [{ color: '#38414e' }],
},
{
featureType: 'road',
elementType: 'geometry.stroke',
stylers: [{ color: '#212a37' }],
},
{
featureType: 'road',
elementType: 'labels.text.fill',
stylers: [{ color: '#9ca5b3' }],
},
{
featureType: 'road.highway',
elementType: 'geometry',
stylers: [{ color: '#746855' }],
},
{
featureType: 'road.highway',
elementType: 'geometry.stroke',
stylers: [{ color: '#1f2835' }],
},
{
featureType: 'road.highway',
elementType: 'labels.text.fill',
stylers: [{ color: '#f3d19c' }],
},
{
featureType: 'transit',
elementType: 'geometry',
stylers: [{ color: '#2f3948' }],
},
{
featureType: 'transit.station',
elementType: 'labels.text.fill',
stylers: [{ color: '#d59563' }],
},
{
featureType: 'water',
elementType: 'geometry',
stylers: [{ color: '#17263c' }],
},
{
featureType: 'water',
elementType: 'labels.text.fill',
stylers: [{ color: '#515c6d' }],
},
{
featureType: 'water',
elementType: 'labels.text.stroke',
stylers: [{ color: '#17263c' }],
},
],
}}
/>
</div>
);
}
export default function SignIn(props) {
return (
<div style={{
height: "100%",
width: "100%",
}}>
height: '100%',
width: '100%',
}}
>
<Scrollbars autoHeightMin="100%" autoHeightMax="100%">
<Grid container style={{padding: 30}}>
<Grid container style={{ padding: 30 }}>
<Grid item xs={12}>
<DeviceLastSeenMap />
</Grid>
<Grid item xs={12}>
<DrivesTable dongleId={props.device.dongle_id}/>
<DrivesTable dongleId={props.device.dongle_id} />
</Grid>
</Grid>
@ -131,5 +127,3 @@ export default function SignIn(props) {
);
}

View File

@ -5,35 +5,26 @@ import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import React, { useEffect } from 'react';
const stylezz = {
margin: '0px 2px 0px 0px',
}
};
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
const seconds = Math.floor((new Date() - date) / 1000);
if (seconds / 86400 > 1) {
return Math.floor(seconds / 86400) + `d`;
} else if (seconds / 3600 > 1) {
return Math.floor(seconds / 3600) + `h`;
} else if (seconds / 60 > 1) {
return Math.floor(seconds / 60) + `m`;
} else {
return "just now";
return `${Math.floor(seconds / 86400)}d`;
} if (seconds / 3600 > 1) {
return `${Math.floor(seconds / 3600)}h`;
} if (seconds / 60 > 1) {
return `${Math.floor(seconds / 60)}m`;
}
return 'just now';
}
export default function SignIn(props) {
const [state, setState] = React.useState({ count: 0, last_seen: 0 });
const device = props.device;
const { device } = props;
// Reloads component to update X time ago
// TODO prevent X time ago from being refreshed when the device has been
@ -41,21 +32,18 @@ export default function SignIn(props) {
useEffect(() => {
setInterval(() => {
setState({ ...state, count: state.count + 1 })
}, 60 * 1000)
setState({ ...state, count: state.count + 1 });
}, 60 * 1000);
});
const deviceLastSeen = timeSince(new Date(device.last_seen));
return (
<div>
<ButtonBase style={{ padding: '10px' }}>
<Grid container spacing={2}>
<Grid item xs={4}>
<img src="/c3.webp" style={{ width: "100%" }} alt="device icon"/>
<img src="/c3.webp" style={{ width: '100%' }} alt="device icon" />
</Grid>
<Grid item xs={8} style={{ textAlign: 'left' }}>
{/* <TextField
@ -77,16 +65,17 @@ export default function SignIn(props) {
}}
fullWidth
/>*/}
<Typography variant="body2" align={"left"} gutterBottom>Dongle: {device.dongle_id}</Typography>
/> */}
<Typography variant="body2" align="left" gutterBottom>
Dongle:
{device.dongle_id}
</Typography>
<div>
{device.online ?
<Chip style={{ background: '#004d40', ...stylezz }} label="Online" size="small" variant="outlined" /> :
<Chip style={{ background: '#b71c1c', ...stylezz }} label={`Offline ${deviceLastSeen}`} size="small" variant="outlined" />
}
{device.online
? <Chip style={{ background: '#004d40', ...stylezz }} label="Online" size="small" variant="outlined" />
: <Chip style={{ background: '#b71c1c', ...stylezz }} label={`Offline ${deviceLastSeen}`} size="small" variant="outlined" />}
<Chip style={{ background: '#004d40', ...stylezz }} label="Active" size="small" variant="outlined" />
</div>
@ -98,5 +87,3 @@ export default function SignIn(props) {
);
}

View File

@ -13,39 +13,36 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect } from 'react';
import { context as DeviceContext } from "./../../../context/devices";
import { context as SnackbarContext } from "./../../../context/toast";
import * as deviceController from "./../../../controllers/devices";
import * as helpers from "./../../../controllers/helpers"
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
function loading() {
return (
<TableRow>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
</TableRow>
)
}
return (
<TableRow>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
</TableRow>
);
}
export default function EnhancedTable(props) {
const [state, dispatch] = useContext(DeviceContext)
const [state, dispatch] = useContext(DeviceContext);
const [, notifDispatch] = useContext(SnackbarContext)
const [, notifDispatch] = useContext(SnackbarContext);
useEffect(() => {
deviceController.getBootlogs(props.dongleId).then((res) => {
setTimeout(() => {
dispatch({ type: "update_dongle_bootlogs", dongle_id: props.dongleId, bootlogs: res.data })
}, 1)
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
}, 1);
}).catch(() => {
notifDispatch({type: "NEW_TOAST", msg: 'Failed to load bootlogs'})
})
}, [dispatch, notifDispatch, props.dongleId])
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load bootlogs' });
});
}, [dispatch, notifDispatch, props.dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -54,51 +51,48 @@ export default function EnhancedTable(props) {
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={'small'}
size="small"
>
<TableHead>
<TableRow>
<TableCell >Date</TableCell>
<TableCell >File</TableCell>
<TableCell >File size</TableCell>
<TableCell >Actions</TableCell>
<TableCell>Date</TableCell>
<TableCell>File</TableCell>
<TableCell>File size</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{/* if you don't need to support IE11, you can replace the `stableSort` call with:
rows.slice().sort(getComparator(order, orderBy)) */}
{state.dongles[props.dongleId].boot ? state.dongles[props.dongleId].boot.map((row) => {
return (
<TableRow hover>
<TableCell >{helpers.formatDate(row.date)}</TableCell>
<TableCell >{row.name}</TableCell>
<TableCell >{Math.round(row.size / 1024) + ' MiB'}</TableCell>
<TableCell>
{state.dongles[props.dongleId].boot ? state.dongles[props.dongleId].boot.map((row) => (
<TableRow hover>
<TableCell>{helpers.formatDate(row.date)}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(row.permalink, "_blank")}>
<OpenInNewIcon fontSize="inherit"/>
<IconButton size="small" onClick={() => window.open(row.permalink, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Preserve">
<IconButton size="small">
<FavoriteBorderIcon fontSize="inherit"/>
<FavoriteBorderIcon fontSize="inherit" />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton size="small">
<DeleteIcon fontSize="inherit"/>
</IconButton>
<DeleteIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
)
}) : [1, 1, 1, 1, 1].map(loading) }
</TableCell>
</TableRow>
)) : [1, 1, 1, 1, 1].map(loading) }
</TableBody>
</Table>
</TableContainer>
@ -106,4 +100,3 @@ export default function EnhancedTable(props) {
</Box>
);
}

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
import DeleteIcon from '@mui/icons-material/Delete';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
@ -15,13 +15,9 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect } from 'react';
import { context as DeviceContext } from "./../../../context/devices";
import * as deviceController from "./../../../controllers/devices";
import * as helpers from "./../../../controllers/helpers"
import { context as DeviceContext } from '../../../context/devices';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
function buildContent(row) {
return (
@ -29,15 +25,13 @@ function buildContent(row) {
hover
>
<TableCell >{helpers.formatDate(row.date)}</TableCell>
<TableCell >{row.name}</TableCell>
<TableCell >{Math.round(row.size / 1024) + ' MiB'}</TableCell>
<TableCell>{helpers.formatDate(row.date)}</TableCell>
<TableCell>{row.name}</TableCell>
<TableCell>{`${Math.round(row.size / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(row.permalink, "_blank")}>
<IconButton size="small" onClick={() => window.open(row.permalink, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
@ -56,31 +50,31 @@ function buildContent(row) {
</TableCell>
</TableRow>
)
);
}
function loading() {
return (
<TableRow>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
</TableRow>
)
);
}
export default function EnhancedTable(props) {
// eslint-disable-next-line no-unused-vars
const [state, dispatch] = useContext(DeviceContext)
const [state, dispatch] = useContext(DeviceContext);
useEffect(() => {
deviceController.getCrashlogs(props.dongleId).then((res) => {
dispatch({ type: "update_dongle_bootlogs", dongle_id: props.dongleId, bootlogs: res.data })
})
dispatch({ type: 'update_dongle_bootlogs', dongle_id: props.dongleId, bootlogs: res.data });
});
}, [dispatch, props.dongleId]);
console.log("drives", state.dongles[props.dongleId])
console.log("drives", typeof state.dongles[props.dongleId])
console.log('drives', state.dongles[props.dongleId]);
console.log('drives', typeof state.dongles[props.dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -89,23 +83,21 @@ export default function EnhancedTable(props) {
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={'small'}
size="small"
>
<TableHead>
<TableRow>
<TableCell >Date</TableCell>
<TableCell >File</TableCell>
<TableCell >File size</TableCell>
<TableCell >Actions</TableCell>
<TableCell>Date</TableCell>
<TableCell>File</TableCell>
<TableCell>File size</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{state.dongles[props.dongleId].crash ?
state.dongles[props.dongleId].crash.length > 0 ? state.dongles[props.dongleId].crash.map(buildContent) : <p> No drives </p>
:
[1, 1, 1, 1, 1].map(loading)
}
{state.dongles[props.dongleId].crash
? state.dongles[props.dongleId].crash.length > 0 ? state.dongles[props.dongleId].crash.map(buildContent) : <p> No drives </p>
: [1, 1, 1, 1, 1].map(loading)}
</TableBody>
</Table>
@ -115,4 +107,3 @@ export default function EnhancedTable(props) {
</Box>
);
}

View File

@ -1,47 +1,37 @@
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import Grid from '@mui/material/Grid';
import IconButton from '@mui/material/IconButton';
import Skeleton from "@mui/material/Skeleton";
import Skeleton from '@mui/material/Skeleton';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import React, { useContext } from 'react';
import { context as DeviceContext } from "./../../../context/devices";
import { context as SnackbarContext } from "./../../../context/toast";
import * as helpers from "./../../../controllers/helpers"
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import * as helpers from '../../../controllers/helpers';
export default function SignIn(props) {
const [state] = useContext(DeviceContext)
const [, notifDispatch] = useContext(SnackbarContext)
const [state] = useContext(DeviceContext);
const [, notifDispatch] = useContext(SnackbarContext);
function pubKeyClipboard(newClip) {
navigator.clipboard.writeText(newClip).then(function () {
navigator.clipboard.writeText(newClip).then(() => {
notifDispatch({
type: "NEW_TOAST",
type: 'NEW_TOAST',
open: true,
msg: "Successfully copied to clipboard!"
})
}, function () {
msg: 'Successfully copied to clipboard!',
});
}, () => {
notifDispatch({
type: "NEW_TOAST",
type: 'NEW_TOAST',
open: true,
msg: "Failed to write to clipboard!"
})
msg: 'Failed to write to clipboard!',
});
});
}
if (!state.dongles[props.dongleId]) { return (<p>no</p>) }
if (!state.dongles[props.dongleId]) { return (<p>no</p>); }
const dongle = state.dongles[props.dongleId];
if (!dongle) {
return (
<Grid container>
@ -53,7 +43,7 @@ export default function SignIn(props) {
</Grid>
)
);
}
return (
@ -63,29 +53,47 @@ export default function SignIn(props) {
<Grid container>
<Grid item xs={3}>
<b>Nickname:</b> {dongle.nick_name ? dongle.nick_name : `My ${dongle.device_type}`}<br></br>
<b>Type:</b> {dongle.device_type}<br></br>
<b>Serial:</b> {dongle.serial}<br></br>
<b>IMEI:</b> {dongle.imei}<br></br>
<b>Registered:</b> {helpers.formatDate(dongle.created)}<br></br>
<b>Last Ping:</b> {helpers.formatDate(dongle.last_ping)}<br></br>
<b>Public Key:</b> -----BEGIN PUBLIC KEY-----
<b>Nickname:</b>
{' '}
{dongle.nick_name ? dongle.nick_name : `My ${dongle.device_type}`}
<br />
<b>Type:</b>
{' '}
{dongle.device_type}
<br />
<b>Serial:</b>
{' '}
{dongle.serial}
<br />
<b>IMEI:</b>
{' '}
{dongle.imei}
<br />
<b>Registered:</b>
{' '}
{helpers.formatDate(dongle.created)}
<br />
<b>Last Ping:</b>
{' '}
{helpers.formatDate(dongle.last_ping)}
<br />
<b>Public Key:</b>
{' '}
-----BEGIN PUBLIC KEY-----
<Tooltip title="Copy public key">
<IconButton onClick={() => pubKeyClipboard(dongle.public_key)}>
<ContentCopyIcon />
</IconButton>
</Tooltip>
<br></br>
<br />
<b>Quota Storage: </b>{dongle.storage_used} MB / 200000 MB
<b>Quota Storage: </b>
{dongle.storage_used}
{' '}
MB / 200000 MB
</Grid>
</Grid>
</div>
);
}

View File

@ -13,26 +13,26 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useEffect, useState } from 'react';
import { context as DeviceContext } from "./../../../context/devices";
import { context as SnackbarContext } from "./../../../context/toast";
import * as deviceController from "./../../../controllers/devices";
import * as helpers from "./../../../controllers/helpers"
import ViewDrive from "./view_drive"
import { context as DeviceContext } from '../../../context/devices';
import { context as SnackbarContext } from '../../../context/toast';
import * as deviceController from '../../../controllers/devices';
import * as helpers from '../../../controllers/helpers';
import ViewDrive from './view_drive';
export default function EnhancedTable(props) {
const [deviceState, dispatch] = useContext(DeviceContext)
const [, notifDispatch] = useContext(SnackbarContext)
const [state, setState] = useState({selectedSegment: null})
const [deviceState, dispatch] = useContext(DeviceContext);
const [, notifDispatch] = useContext(SnackbarContext);
const [state, setState] = useState({ selectedSegment: null });
useEffect(() => {
deviceController.getDrives(props.dongleId).then((res) => {
setTimeout(() => {
dispatch({ type: "update_dongle_drive", dongle_id: props.dongleId, drives: res.data })
}, 1)
dispatch({ type: 'update_dongle_drive', dongle_id: props.dongleId, drives: res.data });
}, 1);
}).catch(() => {
notifDispatch({type: "NEW_TOAST", msg: 'Failed to load drives'})
})
}, [dispatch, notifDispatch, props.dongleId])
notifDispatch({ type: 'NEW_TOAST', msg: 'Failed to load drives' });
});
}, [dispatch, notifDispatch, props.dongleId]);
return (
<Box sx={{ width: '100%' }}>
@ -41,7 +41,7 @@ export default function EnhancedTable(props) {
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={'small'}
size="small"
>
<TableHead>
<TableRow>
@ -65,25 +65,27 @@ export default function EnhancedTable(props) {
let metadata;
try {
metadata = JSON.parse(row.metadata)
} catch (err) { metadata = {} }
metadata = JSON.parse(row.metadata);
} catch (err) { metadata = {}; }
return (
<TableRow
hover
onClick={()=>{state.selectedSegment === index ? setState({...state, selectedSegment: null }) : setState({...state, selectedSegment: index })}}
onClick={() => { state.selectedSegment === index ? setState({ ...state, selectedSegment: null }) : setState({ ...state, selectedSegment: index }); }}
>
<TableCell
scope="row"
>{row.identifier}</TableCell>
>
{row.identifier}
</TableCell>
<TableCell >{metadata.hasOwnProperty('CarParams1') ? metadata.CarParams['CarName'] : "Glorious Skoda"}</TableCell>
<TableCell >{metadata.hasOwnProperty('InitData1') ? metadata.InitData['Version'] : "Lemon boy"}</TableCell>
<TableCell >{Math.round(row.filesize / 1024) + ' MiB'}</TableCell>
<TableCell >{helpers.formatDuration(row.duration)}</TableCell>
<TableCell >{Math.round(row.distance_meters / 1000)}</TableCell>
<TableCell >{row.upload_complete.toString()}</TableCell>
<TableCell >{row.is_processed.toString()}</TableCell>
<TableCell >{helpers.formatDate(row.drive_date)}</TableCell>
<TableCell>{metadata.hasOwnProperty('CarParams1') ? metadata.CarParams.CarName : 'Glorious Skoda'}</TableCell>
<TableCell>{metadata.hasOwnProperty('InitData1') ? metadata.InitData.Version : 'Lemon boy'}</TableCell>
<TableCell>{`${Math.round(row.filesize / 1024)} MiB`}</TableCell>
<TableCell>{helpers.formatDuration(row.duration)}</TableCell>
<TableCell>{Math.round(row.distance_meters / 1000)}</TableCell>
<TableCell>{row.upload_complete.toString()}</TableCell>
<TableCell>{row.is_processed.toString()}</TableCell>
<TableCell>{helpers.formatDate(row.drive_date)}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
@ -106,13 +108,11 @@ export default function EnhancedTable(props) {
</TableCell>
</TableRow>
)
);
})
}) :
[1, 1, 1, 1, 1].map((v) => (
<TableRow
>
: [1, 1, 1, 1, 1].map((v) => (
<TableRow>
<TableCell padding="checkbox">
<Skeleton animation="wave" />
</TableCell>
@ -122,19 +122,16 @@ export default function EnhancedTable(props) {
<Skeleton animation="wave" />
</TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell ><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
<TableCell><Skeleton animation="wave" /></TableCell>
</TableRow>
))
}
))}
</TableBody>
</Table>
@ -145,4 +142,3 @@ export default function EnhancedTable(props) {
</Box>
);
}

View File

@ -2,17 +2,16 @@ import Box from '@mui/material/Box';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import React from 'react';
import BootLogsTable from "./boot";
import BootLogsTable from './boot';
import Console from './console';
import CrashLogsTable from "./crash";
import CrashLogsTable from './crash';
import DeviceInfo from './device';
import DrivesLogTable from "./drives";
import DrivesLogTable from './drives';
function TabPanel(props) {
const { children, value, index, ...other } = props;
const {
children, value, index, ...other
} = props;
return (
<div
@ -31,10 +30,7 @@ function TabPanel(props) {
);
}
export default function SignIn(props) {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
@ -45,8 +41,13 @@ export default function SignIn(props) {
<div className="wrapper">
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example" variant="scrollable"
scrollButtons="auto">
<Tabs
value={value}
onChange={handleChange}
aria-label="basic tabs example"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Device" />
<Tab label="Drives" />
<Tab label="Crashes" />
@ -72,10 +73,7 @@ export default function SignIn(props) {
{
}
</div>
);
}

View File

@ -10,177 +10,178 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Tooltip from '@mui/material/Tooltip';
import React, { useContext, useState } from 'react';
import { context as DeviceContext } from "./../../../context/devices";
import * as deviceController from "./../../../controllers/devices";
import Typography from "@mui/material/Typography"
import Typography from '@mui/material/Typography';
import { context as DeviceContext } from '../../../context/devices';
import * as deviceController from '../../../controllers/devices';
export default function EnhancedTable(props) {
const [deviceState] = useContext(DeviceContext)
const [deviceState] = useContext(DeviceContext);
const [state, setState] = useState({ loading: true, firstReqSent: false, segment: null, drive: null })
const [state, setState] = useState({
loading: true, firstReqSent: false, segment: null, drive: null,
});
if (state.drive === null) {
setState({ ...state, drive: props.drive })
if (state.drive === null) {
setState({ ...state, drive: props.drive });
}
if (props.drive !== state.drive) {
setState({
...state, loading: true, firstReqSent: false, segment: null, drive: props.drive,
});
}
const dongle_id = props.dongleId;
const drive_id = props.drive;
const dongle = deviceState.dongles[dongle_id];
console.log('view drive', dongle);
console.log('drives', dongle.drives);
if (!dongle || !dongle.drives) return (<p>loading</p>);
if (state.segment === null) {
// TODO Make this not run multiple times
deviceController.getDriveSegments(dongle_id, dongle.drives[drive_id].identifier).then((res) => {
console.log('my res', res.data);
if (res.data === null) {
setState({
...state, loading: false, firstReqSent: true, segment: [],
});
} else {
setState({
...state, loading: false, firstReqSent: true, segment: res.data,
});
}
});
}
// test
const drive = dongle.drives[drive_id];
let vehicle = '';
let version = '';
let gitRemote = '';
let gitBranch = '';
let gitCommit = '';
let metadata = {};
try {
metadata = JSON.parse(drive.metadata);
if (metadata.InitData) {
version = metadata.InitData.Version || 'Unknown';
gitRemote = metadata.InitData.GitRemote || 'Unknown';
gitBranch = metadata.InitData.GitBranch || 'Unknown';
gitCommit = metadata.InitData.GitCommit || 'Unknown';
}
if (props.drive !== state.drive) {
setState({ ...state, loading: true, firstReqSent: false, segment: null, drive: props.drive })
if (metadata.CarParams) {
if (metadata.CarParams.CarName !== undefined) vehicle += `${metadata.CarParams.CarName.toUpperCase()} `;
if (metadata.CarParams.CarFingerprint !== undefined) vehicle += (metadata.CarParams.CarFingerprint.toUpperCase());
}
} catch (exception) { console.log(exception); }
// const directoryTree = dirTree(config.storagePath + device.dongle_id + "/" + dongleIdHash + "/" + driveIdentifierHash + "/" + drive.identifier);
const directoryTree = state.segment;
const driveUrl = 'driveurl';
const directorySegments = {};
if (directoryTree) {
for (const i in directoryTree.children) {
// skip any non-directory entries (for example m3u8 file in the drive directory)
if (directoryTree.children[i].type !== 'directory') continue;
const segment = directoryTree.children[i].name;
const logSegment = {};
for (const c in directoryTree.children[i].children) {
logSegment[directoryTree.children[i].children[c].name] = {
url: `${driveUrl}/${segment}/${directoryTree.children[i].children[c].name}`,
name: directoryTree.children[i].children[c].name,
fileSize: directoryTree.children[i].children[c].size,
};
}
directorySegments[segment] = logSegment;
}
const dongle_id = props.dongleId;
const drive_id = props.drive;
const dongle = deviceState.dongles[dongle_id];
console.log("view drive", dongle)
console.log("drives", dongle.drives)
if (!dongle || !dongle.drives) return (<p>loading</p>)
console.log('output is', directorySegments);
}
if (state.segment === null) {
// TODO Make this not run multiple times
deviceController.getDriveSegments(dongle_id, dongle.drives[drive_id].identifier).then((res) => {
console.log("my res", res.data)
if (res.data === null) {
setState({ ...state, loading: false, firstReqSent: true, segment: [] })
} else {
setState({ ...state, loading: false, firstReqSent: true, segment: res.data })
}
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2, padding: '20px' }}>
})
<Typography variant="body1">
<b>Vehicle:</b>
{' '}
{vehicle}
</Typography>
<Typography variant="body1">
<b>Version:</b>
{' '}
{version}
</Typography>
<Typography variant="body1">
<b>gitRemote:</b>
{' '}
{gitRemote}
</Typography>
<Typography variant="body1">
<b>gitBranch:</b>
{' '}
{gitBranch}
</Typography>
<Typography variant="body1">
<b>gitCommit:</b>
{' '}
{gitCommit}
</Typography>
}
<Typography variant="body2"><b>Fingerprint: </b></Typography>
// test
<TableContainer>
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size="small"
>
<TableHead>
<TableRow>
<TableCell>Segment ID</TableCell>
<TableCell>File</TableCell>
<TableCell>File size</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
directorySegments ? Object.keys(directorySegments).map((key, index) => Object.keys(directorySegments[key]).map((key1, index1) => (
<TableRow hover>
<TableCell>{key}</TableCell>
<TableCell>{directorySegments[key][key1].name}</TableCell>
<TableCell>{`${Math.round(directorySegments[key][key1].fileSize / 1024)} MiB`}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, '_blank')}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
const drive = dongle.drives[drive_id];
let vehicle = "";
let version = "";
let gitRemote = "";
let gitBranch = "";
let gitCommit = "";
let metadata = {};
try {
metadata = JSON.parse(drive.metadata);
if (metadata['InitData']) {
version = metadata['InitData']['Version'] || "Unknown";
gitRemote = metadata['InitData']['GitRemote'] || "Unknown";
gitBranch = metadata['InitData']['GitBranch'] || "Unknown";
gitCommit = metadata['InitData']['GitCommit'] || "Unknown";
}
if (metadata['CarParams']) {
if (metadata['CarParams']['CarName'] !== undefined) vehicle += (metadata['CarParams']['CarName'].toUpperCase()) + " ";
if (metadata['CarParams']['CarFingerprint'] !== undefined) vehicle += (metadata['CarParams']['CarFingerprint'].toUpperCase())
}
} catch (exception) { console.log(exception) }
//const directoryTree = dirTree(config.storagePath + device.dongle_id + "/" + dongleIdHash + "/" + driveIdentifierHash + "/" + drive.identifier);
const directoryTree = state.segment;
const driveUrl = "driveurl"
var directorySegments = {};
if (directoryTree) {
for (var i in directoryTree.children) {
// skip any non-directory entries (for example m3u8 file in the drive directory)
if (directoryTree.children[i].type !== 'directory') continue;
var segment = directoryTree.children[i].name;
let logSegment = {}
for (var c in directoryTree.children[i].children) {
logSegment[directoryTree.children[i].children[c].name] = {
url: `${driveUrl}/${segment}/${directoryTree.children[i].children[c].name}`,
name: directoryTree.children[i].children[c].name,
fileSize: directoryTree.children[i].children[c].size
}
}
directorySegments[segment] = logSegment;
}
console.log("output is", directorySegments)
}
return (
<Box sx={{ width: '100%' }}>
<Paper sx={{ width: '100%', mb: 2, padding: '20px' }}>
<Typography variant="body1"><b>Vehicle:</b> {vehicle}</Typography>
<Typography variant="body1"><b>Version:</b> {version}</Typography>
<Typography variant="body1"><b>gitRemote:</b> {gitRemote}</Typography>
<Typography variant="body1"><b>gitBranch:</b> {gitBranch}</Typography>
<Typography variant="body1"><b>gitCommit:</b> {gitCommit}</Typography>
<Typography variant="body2"><b>Fingerprint: </b></Typography>
<TableContainer>
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={'small'}
>
<TableHead>
<TableRow>
<TableCell >Segment ID</TableCell>
<TableCell >File</TableCell>
<TableCell >File size</TableCell>
<TableCell >Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
directorySegments ? Object.keys(directorySegments).map((key, index) => {
return Object.keys(directorySegments[key]).map((key1, index1) => (
<TableRow hover>
<TableCell >{key}</TableCell>
<TableCell >{directorySegments[key][key1].name}</TableCell>
<TableCell>{Math.round(directorySegments[key][key1].fileSize / 1024) + ' MiB'}</TableCell>
<TableCell>
<Tooltip title="Open in new window">
<IconButton size="small" onClick={() => window.open(directorySegments[key][key1].url, "_blank")}>
<OpenInNewIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))
}) : null
</TableRow>
))) : null
}
</TableBody>
</Table>
</TableContainer>
</TableBody>
</Table>
</TableContainer>
</Paper>
</Box >
);
</Paper>
</Box>
);
}
/*
@ -195,8 +196,6 @@ var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(de
cabanaUrl = config.cabanaUrl + '?retropilotIdentifier=' + device.dongle_id + '|' + dongleIdHash + '|' + drive.identifier + '|' + driveIdentifierHash + '&retropilotHost=' + encodeURIComponent(config.baseUrl) + '&demo=1"';
}
var response = `<html style="font-family: monospace">
<head>
<link href="https://vjs.zencdn.net/7.11.4/video-js.css" rel="stylesheet" />
@ -290,7 +289,6 @@ var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(de
<tr><th>segment</th><th>qcamera</th><th>qlog</th><th>fcamera</th><th>rlog</th><th>dcamera</th><th>processed</th><th>stalled</th></tr>
`;
var directorySegments = {};
for (var i in directoryTree.children) {
// skip any non-directory entries (for example m3u8 file in the drive directory)
@ -298,7 +296,6 @@ var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(de
var segment = directoryTree.children[i].name;
var qcamera = '--';
var fcamera = '--';
var dcamera = '--';
@ -345,4 +342,4 @@ var dongleIdHash = crypto.createHmac('sha256', config.applicationSalt).update(de
<hr/>
<a href="/useradmin/signout">Sign Out</a></body></html>`;
*/
*/

View File

@ -1,16 +1,16 @@
import { createTheme, ThemeProvider } from '@mui/material/styles';
import React, { useContext, useState } from 'react';
import { Link } from "react-router-dom";
import { UserContext } from "./../../context/users";
import { Link } from 'react-router-dom';
import { UserContext } from '../../context/users';
const theme = createTheme();
const theme = createTheme();
export default function SignIn() {
const [loading, setLoading] = useState(false)
const [ state, dispatch ] = useContext(UserContext)
console.log("component", state)
const [loading, setLoading] = useState(false);
const [state, dispatch] = useContext(UserContext);
console.log('component', state);
const handleSubmit = (event) => {
dispatch({ type: "toggle_button" })
dispatch({ type: 'toggle_button' });
event.preventDefault();
const data = new FormData(event.currentTarget);
@ -20,19 +20,15 @@ export default function SignIn() {
password: data.get('password'),
});
setLoading(true)
setLoading(true);
};
return (
<ThemeProvider theme={theme}>
<p>hello</p>
<Link to="/login" variant="body2">
{"login?"}
</Link>
<p>hello</p>
<Link to="/login" variant="body2">
login?
</Link>
</ThemeProvider>
);
}

View File

@ -7,14 +7,14 @@ import Paper from '@mui/material/Paper';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import React, { useContext, useState } from 'react';
import { UserContext } from "./../../context/users";
import { UserContext } from '../../context/users';
export default function SignIn() {
const [loading, setLoading] = useState(false)
const [state, dispatch] = useContext(UserContext)
console.log("component", state)
const [loading, setLoading] = useState(false);
const [state, dispatch] = useContext(UserContext);
console.log('component', state);
const handleSubmit = (event) => {
dispatch({ type: "toggle_button" })
dispatch({ type: 'toggle_button' });
event.preventDefault();
const data = new FormData(event.currentTarget);
@ -24,7 +24,7 @@ export default function SignIn() {
password: data.get('password'),
});
setLoading(true)
setLoading(true);
};
return (
@ -41,13 +41,10 @@ export default function SignIn() {
alignItems: 'center',
}}
style={{
padding: '15px'
padding: '15px',
}}
>
<Typography component="h1" variant="h5" align="left">
Sign in
</Typography>
@ -85,20 +82,15 @@ export default function SignIn() {
</LoadingButton>
<Link href="#" variant="body2">
{"New Here or Forgotten password?"}
New Here or Forgotten password?
</Link>
</Box>
</Paper>
</Grid>
</Grid>
</Container>
);
}

View File

@ -2,52 +2,41 @@ import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import { Scrollbars } from 'rc-scrollbars';
import React, { useContext } from 'react';
import { context as DeviceContext } from "./../../context/devices";
import DeviceData from './../device/deviceData';
import DeviceOverview from "./../device/overview";
import { context as DeviceContext } from '../../context/devices';
import DeviceData from '../device/deviceData';
import DeviceOverview from '../device/overview';
export default function SignIn() {
const [deviceState] = useContext(DeviceContext);
return (
<div className="wrapper">
<Grid container spacing={0} style={{ height: '100%', justify: 'space-around', minHeight: "100%", maxHeight: "100%" }} >
<Grid item xs={12} md={4} lg={3} sm={6} xl={2} style={{ minHeight: "100%", maxHeight: "100%" }}>
<Paper style={{ minHeight: "100%", maxHeight: "100%", margin: "0" }}>
<Scrollbars autoHeight={true} autoHeightMin="calc(100vh - 14px)" autoHeightMax="calc(100% - 14px)">
<Grid
container
spacing={0}
style={{
height: '100%', justify: 'space-around', minHeight: '100%', maxHeight: '100%',
}}
>
<Grid item xs={12} md={4} lg={3} sm={6} xl={2} style={{ minHeight: '100%', maxHeight: '100%' }}>
<Paper style={{ minHeight: '100%', maxHeight: '100%', margin: '0' }}>
<Scrollbars autoHeight autoHeightMin="calc(100vh - 14px)" autoHeightMax="calc(100% - 14px)">
<div style={{ padding: '5px' }}>
{deviceState ? Object.keys(deviceState.dongles).map(key => <DeviceOverview device={deviceState.dongles[key]} />) : <p>no</p>}
{deviceState ? Object.keys(deviceState.dongles).map((key) => <DeviceOverview device={deviceState.dongles[key]} />) : <p>no</p>}
</div>
</Scrollbars>
</Paper>
</Grid>
<Grid item xs={12} md={8} lg={9} sm={6} xl={10}>
{deviceState.dongles['53331425'] ? <DeviceData device={deviceState.dongles['53331425']} /> : <p>no</p>}
</Grid>
</Grid>
</div>
);
}

View File

@ -1,19 +1,12 @@
import Snackbar from '@mui/material/Snackbar';
import React, { useContext } from 'react';
import { context as DeviceContext } from "./../../context/toast";
import { context as DeviceContext } from '../../context/toast';
export default function Toast(props) {
const [state, dispatch] = useContext(DeviceContext)
const [state, dispatch] = useContext(DeviceContext);
const handleClose = () => {
dispatch({ type: 'CLOSE_TOAST' })
dispatch({ type: 'CLOSE_TOAST' });
};
return (

View File

@ -1,131 +1,125 @@
import React, { createContext, useEffect, useReducer } from "react";
import * as deviceController from "./../../controllers/devices";
import React, { createContext, useEffect, useReducer } from 'react';
import * as deviceController from '../../controllers/devices';
function process(state, action) {
if (action.type !== "ADD_DATA") { return state }
if (action.type !== 'ADD_DATA') { return state; }
switch (action.data.command) {
case "dongle_status":
return {
...state,
dongles: {
...state.dongles,
[action.data.data.dongle_id]: {
...state.dongles[action.data.data.dongle_id],
online: action.data.data.online,
last_seen: action.data.data.time,
dongle_id: action.data.data.dongle_id
}
}
}
default:
return state;
}
switch (action.data.command) {
case 'dongle_status':
return {
...state,
dongles: {
...state.dongles,
[action.data.data.dongle_id]: {
...state.dongles[action.data.data.dongle_id],
online: action.data.data.online,
last_seen: action.data.data.time,
dongle_id: action.data.data.dongle_id,
},
},
};
default:
return state;
}
}
export const Reducer = (state, action) => {
console.log("input", state, action)
switch (action.type) {
case 'ADD_DATA':
return process(state, action);
case "fetch_all_dongles":
console.log("fetch", action)
return {
...state,
dongles: action.data
}
console.log('input', state, action);
switch (action.type) {
case 'ADD_DATA':
return process(state, action);
case 'fetch_all_dongles':
console.log('fetch', action);
return {
...state,
dongles: action.data,
};
case "update_dongle_drive":
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
drives: action.drives
}
}
}
case 'update_dongle_drive':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
drives: action.drives,
},
},
};
case "update_dongle_bootlogs":
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
boot: action.bootlogs
}
}
}
case 'update_dongle_bootlogs':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
boot: action.bootlogs,
},
},
};
case "update_dongle_crashlogs":
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
crash: action.crashlogs
}
}
}
case 'update_dongle_crashlogs':
return {
...state,
dongles: {
...state.dongles,
[action.dongle_id]: {
...state.dongles[action.dongle_id],
crash: action.crashlogs,
},
},
};
case "user_authentication":
return {
...state,
user: action.user
}
case 'user_authentication':
return {
...state,
user: action.user,
};
default:
return state;
}
default:
return state;
}
};
const initialState = {
dongles: {}
dongles: {},
};
const Store = ({ children }) => {
console.log("STORE HAS BEEN RERENDERED")
const [state, dispatch] = useReducer(Reducer, initialState);
function Store({ children }) {
console.log('STORE HAS BEEN RERENDERED');
const [state, dispatch] = useReducer(Reducer, initialState);
useEffect(() => {
const ws = new WebSocket('ws://localhost:81');
useEffect(() => {
const ws = new WebSocket('ws://localhost:81');
ws.onmessage = ({ data }) => {
data = JSON.parse(data)
console.log("Message")
if (data.id) {
dispatch({ type: "ADD_DATA", id: data.id, data: data })
}
};
ws.onmessage = ({ data }) => {
data = JSON.parse(data);
console.log('Message');
if (data.id) {
dispatch({ type: 'ADD_DATA', id: data.id, data });
}
};
deviceController.getAllDevices().then((devices) => {
console.log("store", devices)
deviceController.getAllDevices().then((devices) => {
console.log('store', devices);
dispatch({ type: "fetch_all_dongles", data: devices })
})
dispatch({ type: 'fetch_all_dongles', data: devices });
});
return () => {
try {
ws.close();
} catch (e) { }
};
}, []);
return () => {
try {
ws.close();
} catch (e) { }
};
}, []);
return (
<context.Provider value={[state, dispatch]}>
{children}
</context.Provider>
)
};
return (
<context.Provider value={[state, dispatch]}>
{children}
</context.Provider>
);
}
export const context = createContext(initialState);
export default Store;
export default Store;

View File

@ -1,20 +1,19 @@
import React, {createContext, useReducer} from "react";
import Reducer from './reducer'
import React, { createContext, useReducer } from 'react';
import Reducer from './reducer';
const initialState = {
open: false,
message: null
open: false,
message: null,
};
const Store = ({children}) => {
const [state, dispatch] = useReducer(Reducer, initialState);
return (
<context.Provider value={[state, dispatch]}>
{children}
</context.Provider>
)
};
function Store({ children }) {
const [state, dispatch] = useReducer(Reducer, initialState);
return (
<context.Provider value={[state, dispatch]}>
{children}
</context.Provider>
);
}
export const context = createContext(initialState);
export default Store;
export default Store;

View File

@ -4,17 +4,17 @@ const Reducer = (state, action) => {
return {
...state,
open: action.open,
msg: action.message
msg: action.message,
};
case 'CLOSE_TOAST':
return {
...state,
open: false
}
open: false,
};
default:
return state;
}
};
export default Reducer;
export default Reducer;

View File

@ -1,17 +1,17 @@
import React from "react"
import { reducer, initialState } from "./reducer"
import React from 'react';
import { reducer, initialState } from './reducer';
export const UserContext = React.createContext({
state: initialState,
dispatch: () => null
})
dispatch: () => null,
});
export const UserProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, initialState)
export function UserProvider({ children }) {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<UserContext.Provider value={[ state, dispatch ]}>
{ children }
<UserContext.Provider value={[state, dispatch]}>
{ children }
</UserContext.Provider>
)
}
);
}

View File

@ -1,16 +1,16 @@
export const reducer = (state, action) => {
switch (action.type) {
case "sign_out":
case 'sign_out':
return {
...state,
active: !state.active
}
active: !state.active,
};
default:
return state
return state;
}
}
};
export const initialState = {
signedIn: false,
@ -19,4 +19,4 @@ export const initialState = {
username: null,
JWT: null,
},
}
};

View File

@ -1,9 +1,6 @@
import axios from "axios";
import axios from 'axios';
export async function getSession() {
const req = await axios.get(`http://localhost/retropilot/0/useradmin/session`, {withCredentials: true});
return req.data
}
const req = await axios.get('http://localhost/retropilot/0/useradmin/session', { withCredentials: true });
return req.data;
}

View File

@ -1,50 +1,44 @@
import axios from "axios";
import axios from 'axios';
export async function getDrives(dongleId) {
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/drives/false`, { withCredentials: true });
return req.data
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/drives/false`, { withCredentials: true });
return req.data;
}
export async function getBootlogs(dongleId) {
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/bootlogs`, { withCredentials: true });
return req.data
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/bootlogs`, { withCredentials: true });
return req.data;
}
export async function getCrashlogs(dongleId) {
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/crashlogs`, { withCredentials: true });
return req.data
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/crashlogs`, { withCredentials: true });
return req.data;
}
export async function getDriveSegments(dongleId, drive_identifier) {
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/drives/${drive_identifier}/segment`, { withCredentials: true });
return req.data
const req = await axios.get(`http://localhost/retropilot/0/device/${dongleId}/drives/${drive_identifier}/segment`, { withCredentials: true });
return req.data;
}
export async function getAllDevices() {
const req = await axios.get(`http://localhost/retropilot/0/devices`, { withCredentials: true });
const responseData = req.data
const req = await axios.get('http://localhost/retropilot/0/devices', { withCredentials: true });
const responseData = req.data;
let dongles = {}
let dongles = {};
if (responseData.success === true) {
if (responseData.success === true) {
responseData.data.map((object) => dongles = {
...dongles,
[object.dongle_id]: {
...object,
online: false,
// Show when last connected to api instead Athena by default
last_seen: object.last_ping,
},
});
responseData.data.map((object) => {
return dongles;
}
return dongles = {
...dongles,
[object.dongle_id]: {
...object,
online: false,
// Show when last connected to api instead Athena by default
last_seen: object.last_ping,
}
}
})
return dongles;
}
return null;
}
return null;
}

View File

@ -1,24 +1,20 @@
export function formatDate(timestampMs) {
return new Date(timestampMs).toISOString().replace(/T/, ' ').replace(/\..+/, '');
}
export function formatDuration(durationSeconds) {
durationSeconds = Math.round(durationSeconds);
const secs = durationSeconds % 60;
let mins = Math.floor(durationSeconds / 60);
let hours = Math.floor(mins / 60);
mins = mins % 60;
mins %= 60;
const days = Math.floor(hours / 24);
hours = hours % 24;
hours %= 24;
let response = '';
if (days > 0) response += days + 'd ';
if (hours > 0 || days > 0) response += hours + 'h ';
if (hours > 0 || days > 0 || mins > 0) response += mins + 'm ';
response += secs + 's';
if (days > 0) response += `${days}d `;
if (hours > 0 || days > 0) response += `${hours}h `;
if (hours > 0 || days > 0 || mins > 0) response += `${mins}m `;
response += `${secs}s`;
return response;
}
}

View File

@ -8,7 +8,7 @@ ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
document.getElementById('root'),
);
// If you want to start measuring performance in your app, pass a function

View File

@ -1,6 +1,8 @@
const reportWebVitals = onPerfEntry => {
const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
import('web-vitals').then(({
getCLS, getFID, getFCP, getLCP, getTTFB,
}) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);

View File

@ -1,4 +1,4 @@
import React, {useState, useContext} from 'react';
import React, { useState, useContext } from 'react';
import Avatar from '@mui/material/Avatar';
import LoadingButton from '@mui/lab/LoadingButton';
import CssBaseline from '@mui/material/CssBaseline';
@ -14,20 +14,21 @@ import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { UserContext } from "./context/users"
const theme = createTheme();
import { UserContext } from './context/users';
const theme = createTheme();
export default function SignIn() {
const [ state, dispatch ] = useContext(UserContext)
const [state, dispatch] = useContext(UserContext);
return (
<ThemeProvider theme={theme}>
{console.log("testing", state)}
<p> {JSON.stringify(state)}</p>
{console.log('testing', state)}
<p>
{' '}
{JSON.stringify(state)}
</p>
</ThemeProvider>
);
}