From 34bbc131df54aa90778bfc2251d6f7ae96dca5f2 Mon Sep 17 00:00:00 2001 From: GermanBluefox Date: Wed, 15 Jan 2025 22:26:24 +0000 Subject: [PATCH] Use vite for build --- .eslintignore | 4 - .eslintrc.json | 47 ---- .prettierignore | 2 - .prettierrc.js | 9 - DEBUGGING.md | 14 +- eslint.config.mjs | 27 ++ frontend/eslint.config.mjs | 34 +++ frontend/index.html | 31 ++ frontend/package.json | 58 ++-- frontend/public/index.html | 20 -- frontend/public/manifest.json | 26 +- frontend/src/App.css | 2 - frontend/src/App.js | 343 ----------------------- frontend/src/App.jsx | 450 ++++++++++++++++++++++++++++++ frontend/src/App.test.js | 9 - frontend/src/Comm.js | 9 +- frontend/src/ToggleThemeMenu.js | 19 -- frontend/src/ToggleThemeMenu.jsx | 22 ++ frontend/src/assets/alexaLogo.png | Bin 4614 -> 0 bytes frontend/src/assets/knx.png | Bin 10922 -> 0 bytes frontend/src/assets/lcn.png | Bin 2561 -> 0 bytes frontend/src/assets/remote.png | Bin 19384 -> 0 bytes frontend/src/assets/vis.png | Bin 2104 -> 0 bytes frontend/src/index.css | 26 +- frontend/src/index.js | 34 --- frontend/src/index.jsx | 14 + frontend/src/serviceWorker.js | 189 ++++++------- frontend/tsconfig.json | 36 +++ frontend/vite.config.mjs | 36 +++ package.json | 72 ++--- prettier.config.mjs | 3 + 31 files changed, 847 insertions(+), 689 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json delete mode 100644 .prettierignore delete mode 100644 .prettierrc.js create mode 100644 eslint.config.mjs create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/index.html delete mode 100644 frontend/public/index.html delete mode 100644 frontend/src/App.css delete mode 100644 frontend/src/App.js create mode 100644 frontend/src/App.jsx delete mode 100644 frontend/src/App.test.js delete mode 100644 frontend/src/ToggleThemeMenu.js create mode 100644 frontend/src/ToggleThemeMenu.jsx delete mode 100644 frontend/src/assets/alexaLogo.png delete mode 100644 frontend/src/assets/knx.png delete mode 100644 frontend/src/assets/lcn.png delete mode 100644 frontend/src/assets/remote.png delete mode 100644 frontend/src/assets/vis.png delete mode 100644 frontend/src/index.js create mode 100644 frontend/src/index.jsx create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.mjs create mode 100644 prettier.config.mjs diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 19c359b..0000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -**/.eslintrc.js -doc/ -frontend/ -scripts/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 6e461e9..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "root": true, - "env": { - "es6": true, - "node": true, - "mocha": true - }, - "extends": [ - "eslint:recommended" - ], - "plugins": [], - "rules": { - "indent": [ - "error", - 4, - { - "SwitchCase": 1 - } - ], - "no-console": "off", - "no-unused-vars": [ - "error", - { - "ignoreRestSiblings": true, - "argsIgnorePattern": "^_" - } - ], - "no-var": "error", - "no-trailing-spaces": "error", - "prefer-const": "error", - "quotes": [ - "error", - "single", - { - "avoidEscape": true, - "allowTemplateLiterals": true - } - ], - "semi": [ - "error", - "always" - ] - }, - "parserOptions": { - "ecmaVersion": "latest" - } -} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 515bcd4..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -package.json -package-lock.json \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js deleted file mode 100644 index 164ee34..0000000 --- a/.prettierrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - semi: true, - trailingComma: 'all', - singleQuote: true, - printWidth: 120, - useTabs: false, - tabWidth: 4, - endOfLine: 'lf', -}; diff --git a/DEBUGGING.md b/DEBUGGING.md index f5b5303..a2a5e26 100644 --- a/DEBUGGING.md +++ b/DEBUGGING.md @@ -2,19 +2,19 @@ ## Local Debugging -To test the repochecker and debug into the script under vscode: +To test the adapter-checker and debug into the script under vscode: 1. Clone the repository to your local machine.\ - Best is that the directory is on the same level than your adapter repository. + Best is that the directory is on the same level as your adapter repository. -2. run npm install in the repochecker directory. +2. run npm install in the adapter-checker directory. 3. switch to your adapter repository and create a new launch configuration: ```json5 { "name": "Launch Program", - "program": "../iobroker.repochecker/index.js", // path to the repochecker repo + "program": "../iobroker.repochecker/index.js", // path to the adapter checker repo // args as entered on the commandline, arguments as a array "args": ["https://github.com/klein0r/ioBroker.luftdaten","--local"], "request": "launch", @@ -29,12 +29,12 @@ To test the repochecker and debug into the script under vscode: ## Local Testing without debugging -To test the repochecker under vscode: +To test the adapter checker under vscode: 1. Clone the repository to your local machine.\ - Best is that the directory is on the same level than your adapter repository. + Best is that the directory is on the same level as your adapter repository. -2. run npm install in the repochecker directory. +2. run npm install in the adapter checker directory. 3. switch to your adapter repository and enter the following commandline in a new terminal diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..e026127 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,27 @@ +import config from '@iobroker/eslint-config'; + +export default [ + ...config, + { + languageOptions: { + parserOptions: { + allowDefaultProject: { + allow: ['*.js', '*.mjs'], + }, + tsconfigRootDir: import.meta.dirname, + project: './tsconfig.json', + // projectService: true, + }, + }, + }, + { + // disable temporary the rule 'jsdoc/require-param' and enable 'jsdoc/require-jsdoc' + rules: { + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-param': 'off', + }, + }, + { + ignores: ['build-backend/**/*', 'lib/**/*'], + }, +]; diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs new file mode 100644 index 0000000..ea48839 --- /dev/null +++ b/frontend/eslint.config.mjs @@ -0,0 +1,34 @@ +import config, { reactConfig } from '@iobroker/eslint-config'; + +export default [ + ...config, + ...reactConfig, + { + rules: { + 'no-new-func': 'warn', + 'no-extend-native': 'warn', + 'no-eval': 'warn', + }, + }, + { + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProject: ['*.mjs'], + }, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + // disable temporary the rule 'jsdoc/require-param' and enable 'jsdoc/require-jsdoc' + rules: { + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-param': 'off', + '@/no-duplicate-imports': 'error', + }, + }, + { + ignores: ['build/**/*', 'node_modules/**/*', 'src/serviceWorker.js', 'vite.config.mjs'], + }, +]; diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..850eae7 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,31 @@ + + + + + + + + + + ioBroker Adapter checker + + + +
+ + + diff --git a/frontend/package.json b/frontend/package.json index 01d8be2..bffb1c0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,32 +1,30 @@ { - "name": "frontend-user", - "version": "0.1.0", - "private": true, - "dependencies": { - "@iobroker/adapter-react-v5": "^4.0.2", - "@mui/material": "^5.10.17", - "@mui/icons-material": "^5.10.16", - "@mui/styles": "^5.10.16", - "babel-eslint": "^10.1.0", - "react-moment": "^1.1.2", - "moment": "^2.29.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-scripts": "^5.0.1" - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": [ - ">0.2%", - "not dead", - "not ie <= 11", - "not op_mini all" - ] + "name": "frontend-user", + "version": "0.1.0", + "private": true, + "dependencies": { + "@iobroker/adapter-react-v5": "^7.4.14", + "@mui/material": "^6.4.0", + "@mui/icons-material": "^6.4.0", + "@vitejs/plugin-react": "^4.3.4", + "react-moment": "^1.1.3", + "moment": "^2.30.1", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "scripts": { + "start": "vite", + "build": "vite build", + "lint": "eslint -c eslint.config.mjs", + "npm": "npm i --force" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] } diff --git a/frontend/public/index.html b/frontend/public/index.html deleted file mode 100644 index 49b24ff..0000000 --- a/frontend/public/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - ioBroker Adapter checker - - - -
- - diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json index bea54e1..b501703 100644 --- a/frontend/public/manifest.json +++ b/frontend/public/manifest.json @@ -1,15 +1,15 @@ { - "short_name": "Repo checker", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" + "short_name": "Adapter checker", + "name": "ioBroker Adapter checker", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" } diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index 748abd5..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,2 +0,0 @@ -.App { -} diff --git a/frontend/src/App.js b/frontend/src/App.js deleted file mode 100644 index 5a51ef7..0000000 --- a/frontend/src/App.js +++ /dev/null @@ -1,343 +0,0 @@ -import React, {Component} from 'react'; -import { withStyles } from '@mui/styles'; -import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'; - -import './App.css'; -import AppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import Input from '@mui/material/Input'; -import Fab from '@mui/material/Fab'; -import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import Button from '@mui/material/Button'; -import CircularProgress from '@mui/material/CircularProgress'; - -import CheckIcon from '@mui/icons-material/DoneOutlined'; -import ErrorIcon from '@mui/icons-material/Cancel'; -import WarningIcon from '@mui/icons-material/Announcement'; - -import Comm from './Comm'; -import ToggleThemeMenu from './ToggleThemeMenu'; -import Utils from '@iobroker/adapter-react-v5/Components/Utils'; -import theme from '@iobroker/adapter-react-v5/Theme'; -import MessageDialog from '@iobroker/adapter-react-v5/Dialogs/Message'; -import I18n from '@iobroker/adapter-react-v5/i18n'; - -const NARROW_WIDTH = 500; - -const styles = theme => ({ - toolbarTitle: { - //position: 'absolute', - top: 0, - right: 20, - whiteSpace: 'nowrap' - }, - urlInput: { - color: 'white' - }, - branchInput: { - width: 100, - marginLeft: 10, - color: 'white' - }, - attrTitle: { - display: 'inline-block', - width: 160, - fontWeight: 'bold', - paddingLeft: 10 - }, - title: { - background: '#faff7c', - padding: 5, - marginTop: 10, - marginBottom: 0 - }, - buttonCheck: { - marginLeft: 10, - marginRight: 20, - }, - body: { - width: '100%', - height: '100%', - overflow: 'hidden', - }, - info: { - padding: 20, - overflow: 'auto', - height: 'calc(100% - 104px)' - }, - ok: { - color: '#111', - }, - error: { - color: '#bf0000' - }, - warning: { - color: '#bf9100' - }, -}); - -class App extends Component { - constructor(props) { - super(props); - - const _theme = theme(Utils.getThemeName()); - - const translations = { - 'en': require('@iobroker/adapter-react-v5/i18n/en'), - 'de': require('@iobroker/adapter-react-v5/i18n/de'), - 'ru': require('@iobroker/adapter-react-v5/i18n/ru'), - 'pt': require('@iobroker/adapter-react-v5/i18n/pt'), - 'nl': require('@iobroker/adapter-react-v5/i18n/nl'), - 'fr': require('@iobroker/adapter-react-v5/i18n/fr'), - 'it': require('@iobroker/adapter-react-v5/i18n/it'), - 'es': require('@iobroker/adapter-react-v5/i18n/es'), - 'pl': require('@iobroker/adapter-react-v5/i18n/pl'), - 'zh-cn': require('@iobroker/adapter-react-v5/i18n/zh-cn'), - }; - - I18n.setTranslations(translations); - I18n.setLanguage((navigator.language || navigator.userLanguage || 'en').substring(0, 2).toLowerCase()); - - this.state = { - url: window.localStorage.getItem('url') || '', - requesting: false, - errors: [], - warnings: [], - result: [], - screenWidth: window.innerWidth, - version: 'Adapter checker', - branch: window.localStorage.getItem('branch') || '', - theme: _theme, - themeName: _theme.name, - themeType: _theme.palette.type, - hasTravis: false, - globalError: null, - }; - - if (window.document.location.search) { - const query = window.document.location.search.replace(/^\?/, ''); - const pairs = query.split('&'); - pairs.forEach(pair => { - const parts = pair.split('='); - if (parts[0] === 'q' && parts[1]) { - this.state.url = decodeURIComponent(parts[1]); - } - }); - - setTimeout(() => this.onCheck(), 500); - } - - this.updateWindowDimensions = this.updateWindowDimensions.bind(this); - } - - componentDidMount() { - window.addEventListener('resize', this.updateWindowDimensions()); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.updateWindowDimensions) - } - - updateWindowDimensions() { - this.setState({screenWidth: window.innerWidth}); - } - - onCheck() { - let url = this.state.url; - if (url.match(/\/$/, '')) { - url = url.substring(0, url.length - 1); - } - - this.setState({errors: [], result: [], warnings:[], requesting: true}); - - Comm.check(url, this.state.branch.trim(), (err, data) => { - if (err) { - this.setState({ - errors: data.errors || [], - globalError: err, - warnings: (data && data.warnings) || [], - result: (data && data.checks) || [], - requesting: false, - hasTravis: (data && data.hasTravis) || false, - }); - } else { - this.setState({ - errors: data.errors || [], - warnings: data.warnings || [], - result: data.checks || [], - version: 'v' + data.version, - requesting: false, - hasTravis: data.hasTravis || false, - }); - } - }); - } - - renderResult() { - return - {this.state.result.map((line, i) => - - - - - )} - ; - } - - renderError() { - return - {this.state.errors.length ?
: null} - {this.state.errors.map((line, i) => - - - - - )} -
; - } - - renderWarnings() { - return - {this.state.warnings.map((line, i) => - - - - - )} - ; - } - - onOpen(path) { - let url = this.state.url.replace('https://raw.githubusercontent.com/', 'https://github.com/'); - url = url.replace(/\/$/, '') + path; - const win = window.open(url, '_blank'); - win.focus(); - } - - onOpenLink(href) { - const win = window.open(href, '_blank'); - win.focus(); - } - - onOpenTravis() { - let url = this.state.url.replace('https://raw.githubusercontent.com/', 'https://travis-ci.org/'); - url = url.replace(/\/$/, ''); - const win = window.open(url, '_blank'); - win.focus(); - } - - toggleTheme() { - const themeName = this.state.themeName; - - // dark => blue => colored => light => dark - let newThemeName = themeName === 'dark' ? 'blue' : - (themeName === 'blue' ? 'colored' : - (themeName === 'colored' ? 'light' : 'dark')); - - Utils.setThemeName(newThemeName); - - const _theme = theme(newThemeName); - - this.setState({ - theme: _theme, - themeName: _theme.name, - themeType: _theme.palette.type - }); - } - - showError() { - if (this.state.globalError) { - return this.setState({globalError: null})} - title={this.state.globalError} - />; - } else { - return null; - } - } - - render() { - const narrowScreen = this.state.screenWidth <= NARROW_WIDTH; - - return - -
- - - { - if (e.key === 'Enter' && this.state.url && !this.state.requesting) { - this.onCheck(); - } - }} - readOnly={this.state.requesting} - className={this.props.classes.urlInput} - style={{maxWidth: narrowScreen ? this.state.screenWidth - 35 : this.state.screenWidth - 250, width: narrowScreen ? 'calc(100% - 35px)' : 'calc(100% - 350px)'}} - onChange={e => { - window.localStorage.setItem('url', e.target.value); - this.setState({url: e.target.value}); - }} - /> - {!narrowScreen ? { - if (e.key === 'Enter' && this.state.url && !this.state.requesting) { - this.onCheck(); - } - }} - readOnly={this.state.requesting} - className={this.props.classes.branchInput} - onChange={e => { - window.localStorage.setItem('branch', e.target.value); - this.setState({branch: e.target.value}); - }} - /> : null} - { - this.state.requesting ? - : - this.onCheck()} aria-label="Check"> - } - {!narrowScreen ?

{this.state.version}

: null} - {!narrowScreen ? this.toggleTheme()} - themeName={this.state.themeName} - t={w => w} - /> : null} -
-
-
- {this.state.result.length ? [ - , - , - , - this.state.hasTravis ? : null, - this.state.errors && this.state.errors.length ? : null, - ] : null} - {this.state.errors ? this.renderError() : null} - {this.state.warnings ? this.renderWarnings() : null} - {this.state.result ? this.renderResult() : null} -
- {this.showError()} -
-
-
; - } -} - -export default withStyles(styles)(App); diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..661a981 --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,450 @@ +import React, { Component } from 'react'; +import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'; + +import { + AppBar, + Toolbar, + Input, + Fab, + List, + ListItem, + ListItemIcon, + ListItemText, + Button, + CircularProgress, +} from '@mui/material'; + +import { DoneOutlined as CheckIcon, Cancel as ErrorIcon, Announcement as WarningIcon } from '@mui/icons-material'; + +import { Utils, Theme, DialogMessage, I18n } from '@iobroker/adapter-react-v5'; + +import Comm from './Comm'; +import ToggleThemeMenu from './ToggleThemeMenu'; + +import en from '@iobroker/adapter-react-v5/i18n/en'; +import de from '@iobroker/adapter-react-v5/i18n/de'; +import ru from '@iobroker/adapter-react-v5/i18n/ru'; +import pt from '@iobroker/adapter-react-v5/i18n/pt'; +import nl from '@iobroker/adapter-react-v5/i18n/nl'; +import fr from '@iobroker/adapter-react-v5/i18n/fr'; +import it from '@iobroker/adapter-react-v5/i18n/it'; +import es from '@iobroker/adapter-react-v5/i18n/es'; +import pl from '@iobroker/adapter-react-v5/i18n/pl'; +import zhCN from '@iobroker/adapter-react-v5/i18n/zh-cn'; + +const NARROW_WIDTH = 500; + +const styles = { + toolbarTitle: { + top: 0, + right: 20, + whiteSpace: 'nowrap', + }, + urlInput: { + color: 'white', + }, + branchInput: { + width: 100, + marginLeft: 10, + color: 'white', + }, + attrTitle: { + display: 'inline-block', + width: 160, + fontWeight: 'bold', + paddingLeft: 10, + }, + title: { + background: '#faff7c', + padding: 5, + marginTop: 10, + marginBottom: 0, + }, + buttonCheck: { + marginLeft: 10, + marginRight: 20, + }, + body: { + width: '100%', + height: '100%', + overflow: 'hidden', + }, + info: { + padding: 20, + overflow: 'auto', + height: 'calc(100% - 104px)', + }, + ok: { + color: '#111', + }, + error: { + color: '#bf0000', + }, + warning: { + color: '#bf9100', + }, +}; + +class App extends Component { + constructor(props) { + super(props); + + const theme = Theme(Utils.getThemeName()); + + const translations = { + en, + de, + ru, + pt, + nl, + fr, + it, + es, + pl, + 'zh-cn': zhCN, + }; + + I18n.setTranslations(translations); + I18n.setLanguage((navigator.language || navigator.userLanguage || 'en').substring(0, 2).toLowerCase()); + + this.state = { + url: window.localStorage.getItem('url') || '', + requesting: false, + errors: [], + warnings: [], + result: [], + screenWidth: window.innerWidth, + version: 'Adapter checker', + branch: window.localStorage.getItem('branch') || '', + theme, + themeName: theme.name, + themeType: theme.palette.mode, + hasTravis: false, + globalError: null, + }; + + if (window.document.location.search) { + const query = window.document.location.search.replace(/^\?/, ''); + const pairs = query.split('&'); + pairs.forEach(pair => { + const parts = pair.split('='); + if (parts[0] === 'q' && parts[1]) { + this.state.url = decodeURIComponent(parts[1]); + } + }); + + setTimeout(() => this.onCheck(), 500); + } + + this.updateWindowDimensions = this.updateWindowDimensions.bind(this); + if (theme.palette.mode === 'dark') { + window.document.body.style.background = '#111'; + } + } + + componentDidMount() { + window.addEventListener('resize', this.updateWindowDimensions()); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.updateWindowDimensions); + } + + updateWindowDimensions() { + this.setState({ screenWidth: window.innerWidth }); + } + + onCheck() { + let url = this.state.url; + if (url.match(/\/$/, '')) { + url = url.substring(0, url.length - 1); + } + + this.setState({ errors: [], result: [], warnings: [], requesting: true }); + + Comm.check(url, this.state.branch.trim(), (err, data) => { + if (err) { + this.setState({ + errors: data.errors || [], + globalError: err, + warnings: (data && data.warnings) || [], + result: (data && data.checks) || [], + requesting: false, + hasTravis: (data && data.hasTravis) || false, + }); + } else { + this.setState({ + errors: data.errors || [], + warnings: data.warnings || [], + result: data.checks || [], + version: 'v' + data.version, + requesting: false, + hasTravis: data.hasTravis || false, + }); + } + }); + } + + renderResult() { + return ( + + {this.state.result.map((line, i) => ( + + + + + + + ))} + + ); + } + + renderError() { + return ( + + {this.state.errors.length ?
: null} + {this.state.errors.map((line, i) => ( + + + + + + + ))} +
+ ); + } + + renderWarnings() { + return ( + + {this.state.warnings.map((line, i) => ( + + + + + + + ))} + + ); + } + + onOpen(path) { + let url = this.state.url.replace('https://raw.githubusercontent.com/', 'https://github.com/'); + url = url.replace(/\/$/, '') + path; + const win = window.open(url, '_blank'); + win.focus(); + } + + onOpenLink(href) { + const win = window.open(href, '_blank'); + win.focus(); + } + + onOpenTravis() { + let url = this.state.url.replace('https://raw.githubusercontent.com/', 'https://travis-ci.org/'); + url = url.replace(/\/$/, ''); + const win = window.open(url, '_blank'); + win.focus(); + } + + toggleTheme() { + const themeName = this.state.themeName; + + // dark => blue => colored => light => dark + let newThemeName = themeName === 'dark' ? 'light' : 'dark'; + + Utils.setThemeName(newThemeName); + + const theme = Theme(newThemeName); + + if (theme.palette.mode === 'dark') { + window.document.body.style.background = '#111'; + } else { + window.document.body.style.background = '#EEE'; + } + this.setState({ + theme, + themeName: theme.name, + themeType: theme.palette.type, + }); + } + + showError() { + if (this.state.globalError) { + return ( + this.setState({ globalError: null })} + text={this.state.globalError} + /> + ); + } else { + return null; + } + } + + render() { + const narrowScreen = this.state.screenWidth <= NARROW_WIDTH; + + return ( + + +
+ + + { + if (e.key === 'Enter' && this.state.url && !this.state.requesting) { + this.onCheck(); + } + }} + readOnly={this.state.requesting} + style={{ + ...styles.urlInput, + maxWidth: narrowScreen + ? this.state.screenWidth - 35 + : this.state.screenWidth - 250, + width: narrowScreen ? 'calc(100% - 35px)' : 'calc(100% - 350px)', + }} + onChange={e => { + window.localStorage.setItem('url', e.target.value); + this.setState({ url: e.target.value }); + }} + /> + {!narrowScreen ? ( + { + if (e.key === 'Enter' && this.state.url && !this.state.requesting) { + this.onCheck(); + } + }} + readOnly={this.state.requesting} + style={styles.branchInput} + onChange={e => { + window.localStorage.setItem('branch', e.target.value); + this.setState({ branch: e.target.value }); + }} + /> + ) : null} + {this.state.requesting ? ( + + ) : ( + this.onCheck()} + aria-label="Check" + > + + + )} + {!narrowScreen ?

{this.state.version}

: null} + {!narrowScreen ? ( + this.toggleTheme()} + themeName={this.state.themeName} + t={w => w} + /> + ) : null} +
+
+
+ {this.state.result.length + ? [ + , + , + , + this.state.hasTravis ? ( + + ) : null, + this.state.errors && this.state.errors.length ? ( + + ) : null, + ] + : null} + {this.state.errors ? this.renderError() : null} + {this.state.warnings ? this.renderWarnings() : null} + {this.state.result ? this.renderResult() : null} +
+ {this.showError()} +
+
+
+ ); + } +} + +export default App; diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js deleted file mode 100644 index a754b20..0000000 --- a/frontend/src/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); diff --git a/frontend/src/Comm.js b/frontend/src/Comm.js index 5ea80ba..81d24e8 100644 --- a/frontend/src/Comm.js +++ b/frontend/src/Comm.js @@ -1,4 +1,7 @@ -const URL = window.API_URL && window.API_URL !== '${API_URL}' ? `${window.API_URL}` : 'https://3jjxddo33l.execute-api.eu-west-1.amazonaws.com/default/checkAdapter'; +const URL = + window.API_URL && window.API_URL !== '${API_URL}' + ? `${window.API_URL}` + : 'https://3jjxddo33l.execute-api.eu-west-1.amazonaws.com/default/checkAdapter'; class Comm { static check(repo, branch, cb) { const url = `${URL}?url=${encodeURIComponent(repo)}${branch ? `&branch=${encodeURIComponent(branch)}` : ''}`; @@ -7,7 +10,7 @@ class Comm { .then(res => res.json()) .then( result => cb && cb(result.error || null, result), - error => cb && cb(error) + error => cb && cb(error), ); } catch (error) { cb && cb(error); @@ -15,4 +18,4 @@ class Comm { } } -export default Comm; \ No newline at end of file +export default Comm; diff --git a/frontend/src/ToggleThemeMenu.js b/frontend/src/ToggleThemeMenu.js deleted file mode 100644 index a779834..0000000 --- a/frontend/src/ToggleThemeMenu.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { IconButton, Tooltip } from '@mui/material'; -import Brightness4Icon from '@mui/icons-material/Brightness4'; -import Brightness5Icon from '@mui/icons-material/Brightness5'; -import Brightness6Icon from '@mui/icons-material/Brightness6'; -import Brightness7Icon from '@mui/icons-material/Brightness7'; - -export default function ToggleThemeMenu({ themeName, toggleTheme, t, className, style, size }) { - return
- - toggleTheme()} size={size || 'medium'}> - {themeName === 'dark' && } - {themeName === 'blue' && } - {themeName === 'colored' && } - {themeName === 'light' && } - - -
; -} \ No newline at end of file diff --git a/frontend/src/ToggleThemeMenu.jsx b/frontend/src/ToggleThemeMenu.jsx new file mode 100644 index 0000000..317eb48 --- /dev/null +++ b/frontend/src/ToggleThemeMenu.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { IconButton, Tooltip } from '@mui/material'; +import { Brightness4 as Brightness4Icon, Brightness7 as Brightness7Icon } from '@mui/icons-material'; + +export default function ToggleThemeMenu({ themeName, toggleTheme, t, className, style, size }) { + return ( +
+ + toggleTheme()} + size={size || 'medium'} + > + {themeName === 'dark' && } + {themeName === 'light' && } + + +
+ ); +} diff --git a/frontend/src/assets/alexaLogo.png b/frontend/src/assets/alexaLogo.png deleted file mode 100644 index 8616b121510bd54c6461fdc32fd1d249cd3da756..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4614 zcmZu#c|6qL*GD17ZbVtLjv=xaWf;m*wn4HlgM{X5Cu@Z;wlI>$WF5Pau?$9bYWUhK zJ0*K#2-%ls`n{e%p67Wzuh+ewd+#~-yw7=k?&qHKeti;5jrCZWuQJon(6Acl>%ypG z-QRwRf%?2Nz@SJSEKKj=FG&EcRe>?4*yKo3q$QN+;QGoeB z?g7Eher_~A&QA~latOBo5oI|AITcsEM^t3a7z15xix7tm%(El`J)T~j#?519LSuD% zz)&chyZtrD2_eoB^T?_DI&BnyUf6Zp3bj$JT6PCsvxT1 z8jne)=#(zZEjoOOZG1|4=Mej~~-!q%xHhBQyFd%S}8e`Krrcxp2gbdeM-X8=K zLtVyGgt+j8;9YAWG2%u-pOEhOdME3$NR_iUIR@;63>enbgW zUGCF$D^!G8H%;{ADX2f3kv87YFk3SRq&xYj+;uC)`}3h1YCT&T!@TkAd4BAJ6=dg8 zZ6s~a%dUdDZsD8nyEcAy{V_jmIY<>KhUAGe zVoIBsFjto(!pe0sMY_R;8%j6B4M9mMe>P2YKD%JcF7vwRDJ=ScjcQs!h?J58LOu*n zpIKysQ$$Mipt>r)FtLi{xg9o{DwH!LA2i%&eC+w^DB{xDjgKZcTk|Obj<#8_fc{p+ zPyb@Hr?FeohWp0L5SPHIHsSO`E`Vg=H<~^g8%Xst2?w4KwjP(EQLM5T{X1}zCi*Hn0!|`Fh3U63X=MnO@ z{OA+bVJRQ8qzqP4kk2S3js-%)pyBlL;(N0A9vLC;X!VKtb$y#0g$sMi3j?qpTm8E) z#W#{JA7R>ZZKevS&g1y~-!$CflCwwKq#l4zsrozXszdTK8!Bmkeyx-H)`%4qM&s%lVB5 z4cg-*w{q*!{(M2nm4tiALjzihyT6|;uh%v56Thh&uNp!4W(xt()dn*R+ft0=JCX!_ z(ZdmN*5&%oiJVv^_AdHnb6Kw{JbDuQBQIxY<~u*D#gUtD`kmz2D>qlGE*y=YzwUEL z^r$5{XPemLl%II|MczUohEk-Ews?&TfR8qdR*`P^ zoCE~9dBl&M(1W7H&}5N0B11D|0ASP8c?_gG#yHQZcziwB)fuMGe(mzp)?$U+>tu#d z8UB3iPHX}p(KY-!fTv*dRwhpPqUEF(BF(hb>@|RVS9z5Kd-!XjrBhcjDhnuI4V|q$ z?8uuL(Fd2RS>G>w;_Z;lXus;u{uxF-@o<_89j;=t@<^WcABlI2QbAiGVsLA1rQcrg zEw*csv3h7aB>k?a#+bo|fwzry433oRxDw9MN3S|->@ofDV&xlqE$0frA20GbYEWcH`yT@tafYGSd{E zY_hCsMI}eKPX*9+AK#D(rweC^F%=odLqZ+R1p3`Gd7xvo!=Wkt3PCb`tg6#&9zwJN z`;Zw=vSV)e6JHb63X38EGv!GYm&)07rwTSjWcmq9K8Nw$j42&^%Tf2f@n_H$`$WGO zqgSk$WX8CUL&al}wGeel4M$`X+^Bxxb!1%*|HTyyGpQwbqjf>m8|M4()SVu|Vazca z7?xFF=bZ6_PU>UzTP-O2tnzSkLg-{7c7h08Vnzcpn&6`0L!2LS;r__StE}mEj42k& zhV6+o1>0Kz9lm;E+i!+L)y;I6$+?-M4=x%PPbKn5w z{n8bwR$pRh+??JTj1bZ!TT}}376`bQ4@6h&LrDf|_^46K=>c=f-Bo`z$wdydr_hw1 zb--xsgVuxr5bMvK4&mS*kPq4J`Qz&8(KuZVFT6C?IlS#1){ct7d4*`^HyG>l_3Zrs z=*4sOLyU!$oNiOM`@6#1i}!p66Ov{--BDV62S>e@OHYIB)kwKL)b;lP;hv*%aG!+g zn1GK<+oE0IimY^XAAs5hQbX_Zm`~MMp4D0yy z#V*vWaZgeW#MXArvFiQ>l8mE1za}Nsm$U+uZ~SMwt8$;TsySqJCDPunyu6_MEW-%& z;$?H&!*XJIL5>UepXdrwzlO&N;%&blN-S^Wx1T#;35YHgwlgYQv~bHNuXsb5fKkcT zZ}166`EM-#f`}h8piSv|Y=dh_ME_3{5$WcZu}%@MsKC(ULj6E1>(A1vW)S_Bin}FX zW~;Th$rvtBYlB+ef4du#6yNh!&A-UJ?on$O%oF=p{8J`Bu>a+j_8rWvwz-_r$(TX5 zcw#K{GC>9XvijE>D+Cy7lLChfNusp0p^mcS z_9`l;QwYMMMc||`o=L*gC-@9Ub>yblCArRMHD*E$tKVVp@9x=i7>?-Kxk+)NhVUDF zr=2S7m_2lKnYGL$HPj{?Y2QDx9+75LUn2Bu{C&~kH&BoMv5dXk+_YBc>p6dodNQG| zwvE)9C;K&v+f6DKMTxG_vAs-1>i+rncuAVl z(i9NYSbueSxd*Jqv@VtH=xF?9SCt{Ay2SprfSn$r7XFF^$7=f+sAdd?vE*5AnM`)S zbaP?FGu9;of~Gncre-ymeG>;)^I=tcIASi2Ze1MPzIyOZ)sWex_Fk&q>Zl#3YT-q? zt5@X3V#H5^cBa?fe7Xi0Y=Vuurozn)yOPKh$aA5YqENql5!KsL;X($S{KwZ+3zb>c z7ph=_O&pY7X8{B4;Q$ko0dFVwpWHvgc{j9vk)Ken_m_Na$*APjBC)`*NgGwHvMy@y z_JeYME9Uh_gzX};4g;&sja6H3!zY(!q=(Yhdoy3Ehh#iApG&#dTSW==yf%S?DJj2Q z@Hz#_R2HT&1!&C4+*~)7)W|iGR^i?!%XF*z7KyM|3k#wnlBg-;Ztrp-d~eR%xGZ6x zr>7?V)D4DdXP%dCHMQ6yvrORHWA&ntLX--2 z*s!%*CptUnBZ3D+V3bworW%BZPzZmY6sX&K#!BeYs5R)h@P&s1zG?ae^4R|eo=UHb zyS~C`bHGLUqqOjV%0}+yAcqD+O+)kKYGZ3~pxtZ{sD8*b1F5t6G#&U)7SjCKSd~{Z zK-^ZU<{>!#zL^Zyzo;%+=c30ZZ(KGWF|NtAOHRKS%Ir{kUGwwTK9Jy2Ye?@g;x9Pc zGvO-il(F$6W^Oln#BfNT-tW4gkM9LaNN(D~uZQ1oSLG>#lGF4yL;g$9Cf<#OZfZH= z;15FrtyZAMA4q$?3$DIaz=e4L^O8YYO||P-pCV}9Gi0je%%h_>;C9-4)<6SyZhz1+ zfK~(eXL|+z#4RC@e@jC8J&vd3kT~fYW=$Y8svwjc29=WfzZ@chVP-$uWgIABLi7ZXDUsli*iXpbk zy{qgkX5?-@&C46tr!CcK%#-t+y-lYlLV0YU@Vlt&yVNF;&R{7JGv)H~^t7u%|G7KC zpk_<-_G|VT+vFZC*x@ak;44V3<7nqcg?wo80POITgm540>Y6HOq)$JR^3z)9PL>_Y! zgX)alA8s?UQ9PV6MVm(j9#zfaaL{NaycyGOSF zY-LHp&yf$0qXm^tMS|*i0!GQFF3-YA6V^6soy$B00s&<;)dbo6P7iux9}U;RXbQ+f zK=b;Wj;z(>d+8rz>`hY`2uErO+5p`zDo84+BO^?Q6_saM+fm;9&~XF{dPBLwB_ECWFL z5s~Z(*L`v(ApY>95?}w5rm2BPoQ=t?T+PNT-ZaSG?YH(-7(Wn^qNcNoxR&kMCkK*w z;lTKHj`_OTmX{3Cn*X=1uZG>CW7^5@$_I`Ex9F-ye)=Zvw}PJ?;$OV6MY2Cq ziskzzDguVP6_m&64DMWGXt_3miJskI6U4;1Qtn3{0Q*7`A;_Ym4A=46;#UYw|FL1V z+oj}Ct8{=%a1JzBbZVI6`pz{{u}OYnjhDy6cQFhY+7v^`$As3U^B&wyG0GBWpOWs~;c?#!B|?@%YZ(r432o1*TvnPxvEGRnrqGQA6c-`d$3*+~zm=VWxTb%3 z@~INMzmLCaq6HiVpMJJcw<7+ronlimX{~FN`e=gH=4eWa75ghWNfE2flh)e~g?S zPEwfJ4sKi}ZV%0YzL>;T5ssU6WI6gShG~@D*Wu2dV0GgI5hQc~uP(O19;8UbM|sjN z(H8Y0bQ8(v9I#6bl53b2HY7+w>`Byso#XVGCqgJX=jKk&`6+|$lk-@Qv|kgrAoe?p zvG~w?OelOCdSdMLjH8y{`F;H&v4wM3icLXaxWhB;OXq{JR8AsBk>m|B5A$4=8P3#_ zjvEiFO)ZX8BYdCN!wYAljqCwu#CZWsc&4uH1wNwil)*IWhkxPww-q7S+xWo;5D8;^ z$pGu3>x%X~R53+Ny83%j$(0vZ&cZLv4=rp~_pWG)uZo}Lz&aOpejUu*Yt!bZ=)(67 zz6q@3K*)msulXYjOv@PVEo6Z$Z6zYhVD*;&XR2W-umh<>hvPo@jEYkK+0Yo=HP*#J HonHPIJ1FT% diff --git a/frontend/src/assets/knx.png b/frontend/src/assets/knx.png deleted file mode 100644 index 267ed3fd578c2a5c764ff35cfc6281a1f5a11ff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10922 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 diff --git a/frontend/src/assets/lcn.png b/frontend/src/assets/lcn.png deleted file mode 100644 index 9f589d4c103df8d6aa9c86e8c726bad2cf99c535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2561 zcmcgu`8yK~9ACy{$dzNFmgdY1d27i?ES47|*Ca=hE5s&6Qc6OTqbMQGlDRT-7Tg0 zwQGtJFt%vX2{2G@pR(2O@lU%Jgn)J0v1i#DBs6avh)CyyZb-lY! zaW|y&U+8?;6+)CxgdgCVPhcoPFVv3!HPSQCGw{{Pwb`9#jIlI#y5Y&m5qENSR&Jx@ z4qC(WexF=348djEvPL9c;y>*G_CvvupEkMWhuJjY`~ z-i-MFAM6DZaC>6-Y|q~h|9E@#2g4stF4WBI_D8ITBwfjY~l{AEzX4s7&oR@iwCF z0X>?V3UW2l3&!dLKYjt)rv+<|uY07?U%*C;DB(+K-_tUy1t?jKf2!CEKN#=yS@Xi# zQfA82vvz7BcRSj~<&uxmqu1zvNu8TlH+oswcyWB@7S~t%fTk$_6>EmTqJ4F0D`Kr@ z!rIf)`42iuDjfYoRV|VW(;MrWFdxZo3VG^w)jF{D*S}M;1+L3S9rMu2kIk@ICrz$W zXpe&P*sf$!%hM{)qo4AAY+~$x4l}KBM|FKCTn>T?q6A~|9D%KDuLbVY7u-J3%6Yc-ef$Z$ojrXQ*(R*j}Dwx(+wtr zfqhKuFmM9vr7xX}8rd;(p6H6ioS)cMha9;zm`?^n5z1oJDiIMkH+gwo_i|4jr^n&h zWsE#PG4B1y;UnQ5D?HqY68}#$V5>5Gh@czHJbFlt8{H=kQhN>aoA+4!?SJTk@EeLJ&Grl!Y)ELh~b$R zC#oytwUr@uGm@d9d61%>{StRfWpypC@Sf4*n05S4nNy}QF*XZ>#n;~S6(n3n0_V}J zXj(0?wYmtgml3rznTHx5^-FJ5L$j{kWmp+ntWelF#ub$|T)(>233cB;1>VWWsE|NPVIdmC$1JxX|HpPes* zNF+{%BD@fEO)@Q|^;|+14F1k(BOu`S8QH6HT}hL*KD-h?S~wq8!JV)F_&%o~A)3+M z-OZouyEc>9sn|-74r5J?qBn2E9Q?W(lcC^&9JDG#8TRp0eeH}Mz)d?w3>UA5DDq@H z6gh%C&y_Nq_}qN2ZJs`RPUe*NLx(%;c!C_MSAJ~(g)8E!ww1AOln3a(d9w`4eEeY* zyqS+uz0yOt9+9IcEuEEF-yCFL`bH#KVWMSp%7{mz_KwOPTXl(aeFp6_qO?jdCd|GcQnu{E?HIIm&jygHXK7qBx$NK%4ZRbPld81!WErfKXle{Z)5 z$K|6|e>8(#62Fpuxd@~*Xr}TDEhq*s-cP0kN8}%74wP+Lr4(1vWo>`<2{+`z7p8RX zExtrmYOe)uPkxAIc;ni)mjBrY>xY4*C7+J7G%#;fyien9H4Eu~^3G2`E>%IxXj96^ z*9zB}8}Uu9gCFRNWs&wW#>^+Mc83fB3)D&{_Z*6|gAZOWYvr(C3$bv5Y1swihRAbf zY5ag;{Vr=*et`rd?P)r)!Xbi-jiXebNDa#1$J__OP5yMtX`r2EC018oov^%sE(~Fp z2g_@CsvIcX&zL)H7L_RlnO0^Qo5jM8%QlRz%`%8$hd(e?WA9kpUvUBKn2w=LqjL_^ z@7AUa4<`_5U)&|Osz_lfv05?X>}HJb_~(2wwITZt89d}4EO;b#-|3a;z3ts&!Rz{l zgAdJclvZO~K3o`*%4rf5HEbpyJ3D>(Fy%FhbXTKYq?Gh%-phrsc^dEQ@(lUt!=B@p zHM|n*o-T=l*u5Q&W17k=Z{HQ8=e3b%8Buc!m(+eK55)(y6^Xgc%q*RUV5J8(8nkF{ zTgHwm7pE@7R!~wE1`CmlF){9P^^%u6erO+5Jj*chZABlydm?EH|+=D0irnF~VS>@%_J5PFehmlD@# z8V6N?ypWbprTta@5jN6~4*03wYS)4_&N}rOiP>E~m92~ufA5nDul)%ajR)I=#tNV; zC-3DkDfsn_eE!g(^eT6r==ZB=wbyP6-6O&Z@V);Rgb}MddtUUa8_>)-_jf-n00wPm JS#icY{$Ko+$LRn7 diff --git a/frontend/src/assets/remote.png b/frontend/src/assets/remote.png deleted file mode 100644 index 9bfc7ae36b6f3407d2f0293bd5dff81a36d2771a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19384 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 b@5xyo0tk{BH0Z`K);guDd0(SK%|7zq*Do{K diff --git a/frontend/src/assets/vis.png b/frontend/src/assets/vis.png deleted file mode 100644 index f52bcd0b7d0142c252991b38243dbf270d8dcf09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2104 zcmah~c{~%0AD(NNO*T0e@s?Kf?vOLN+7KaUKlTz90I1Ir+r1`qphd&XoCyGc80;7LhNlyE4@}tz z6Q>CKP``-iYvI0tuxr=x5oo+`#BUmCRkTj9dZHBoAY66P7<)O!oqM&vN8aRczpe^; zYN6*odqQ|kXoF9X`Sq$0R0cNnss~-oZcmqxm=YTOL9c{8?vk;^Wj7C&pPn%wEICFF za*Oa)0>Wxr)m4wwUs`{Bh7b3ay)~e21Js>WAwJ;c(*|OCJpJ{2>(n2rjl-?sD}Ppk zqdM*Xi&1L~W)fI2etU9_!L1hfxVuvMX)H_8@{ZHK?!;%^sX<3&;ymZd+g<%PTif(c znbt}2?myS4pVG-69~y6@v<;p5$>E`~a6RGG_-{Y5bD-^vJ7-j-JLh7HGt^$4%<;t} zjiS*Le$`!CIYqOC9lCssdyqYl`CD47EpkdNL-g$>(k_y%%VK^Y9Z`}bV5!jXwh#F< zvzz=%;+XWahK7mfw6cVL8h@0flj%BwB$|j<3gVxB(&~}V1R zUxFURV`PZEI`=(ggg7=0(tQbOSD7mLw2BV?QN(Mh6gYxbe@nxoWdM;+L!=E>iM}Pw zGkJsGZmcf7pnHYR?Ch~i6+Qq%cn=WoXTgNw_g3U#+qH1B0EhgN`_ zb4@zGkfJ;s*x^Zgj6->=>K^btN!IqhOqMuTil^O#bsaN3*@@ec_Jjl@D*r$cQ9e1m z7T>%ZTeL^d--Twu)4|0Amj6`J#+)a7!RR~0E4Z%|SYuLqBW<+o0=&ldQVHk1#Phkg zPI?`!iR;#Z3Fn4nej@jt7FuxMcl`CZqX3%~KN|#v!^_bjKjw$cuZgSPdVR>#in8M9 zg=xX|c-}3}K^>>daHG#7!v-LyL9+IHY|T|iFQW{o-6<3J<+LqUN?7C5*mY{OqE(wf zyP*=w3$5C`9@aM|!06F73sA!?{hg0BQ7CtGtLq~5e1@sY=o;3Y)M5lr8s$OlFi*g0 zE!i)|y3Ktq3%Uw$IvnjJ$;&H@Mku_g<8A?0)?Ywj^LThCtbRMb{BNAk(6B-zBfcmh ze)+h+a(!M5Y=t=#kT#032dcO9tRV&RC?Q+N!*9VR$%d|GwV)iDC+xRi3`XF{g}L+?yIdVKc; zSKcX%FZ7m+;-{c~uc2>}TW+s3n}VDOqMAE}BK?^b&9rt(Oz0wSn9)e?WeyYho3rxE zT>^GCPE@>2qd-K)_}jomt^;`L^7j?`1llmYNF+q=^9O6u&H8L@Rig)du6Rvqh!fOnh1>zTmE_+!*7&Q6j@G z%G;_UkD^?2d_$*JiM}aT5a@uRGUFwel9V(QXGQ}z?4n*EHciqED&E-FW$*2rrnQ@qUS#3m^X>PI{fBns7~q^`m+T=KyYfTsLgcU8l*~)9BJ{AtuT&zGomI& zW(D<$C2;ojO9^r|c-r=2kIQXyRAJCDfZk-{DVS3km-^kN+rT##tq~x1U%{JK62u+m zxQLuOf8vKJ5^-hZj7mV}6xF9zB{xD zkSpeQSH4qki?4s@8PVOc5lMtfx}8!yPu{P^3>kYo`*Xr*+EPT(O^C&9^OtLMFX7{M z3L#my6OJ@+;6^~TN&FFH&UF-m4VzPhdy}GygXN;ia&@)=;rb8SE_X~+`hi96fdtx9 z*ZLNaexr=8?(R^Pl)OVl&3)ewRk4;x+9oxt`m#S9S?R>NqKCb?1bfW>DkF}tt&`Py{ z3&s1|C4t2D1H(XSS?`HH8)FMBTCyzt1}}vyS11$z81F61U);@FM4BCZsQ*`r|8?eH c^voo_{VO%=NH%fopx6NyO{|Qc<2+OU1r$p0?*IS* diff --git a/frontend/src/index.css b/frontend/src/index.css index f435003..898e0fe 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,19 +1,19 @@ -html, body, #root { - width: 100%; - height: 100%; - overflow: hidden; +html, +body, +#root { + width: 100%; + height: 100%; + overflow: hidden; } body { - margin: 0; - padding: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + padding: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', + 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/frontend/src/index.js b/frontend/src/index.js deleted file mode 100644 index 03727e5..0000000 --- a/frontend/src/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'; -import './index.css'; -import App from './App'; -import * as serviceWorker from './serviceWorker'; - -import theme from '@iobroker/adapter-react-v5/Theme'; -import Utils from '@iobroker/adapter-react-v5/Components/Utils'; -let themeName = Utils.getThemeName(); - -function build() { - const container = document.getElementById('root'); - const root = createRoot(container); - return root.render( - - - { - themeName = _theme; - build(); - }} - /> - - - ); -} - -build(); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: http://bit.ly/CRA-PWA -serviceWorker.unregister(); diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx new file mode 100644 index 0000000..56a4c63 --- /dev/null +++ b/frontend/src/index.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +const container = document.getElementById('root'); +const root = createRoot(container); +root.render(); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister(); diff --git a/frontend/src/serviceWorker.js b/frontend/src/serviceWorker.js index 012c322..86d6b25 100644 --- a/frontend/src/serviceWorker.js +++ b/frontend/src/serviceWorker.js @@ -11,121 +11,114 @@ // opt-in, read http://bit.ly/CRA-PWA. const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/), ); export function register(config) { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; - } + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit http://bit.ly/CRA-PWA' - ); + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit http://bit.ly/CRA-PWA', + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } }); - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config); - } - }); - } + } } function registerValidSW(swUrl, config) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See http://bit.ly/CRA-PWA.' - ); + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See http://bit.ly/CRA-PWA.', + ); - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } - } - } - }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); } function checkValidServiceWorker(swUrl, config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if (response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.'); }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); } export function unregister() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); - } + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..9596389 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,36 @@ +// Specialized tsconfig for the admin directory, +// includes DOM typings and configures the admin build +{ + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node", + // check JS files + "allowJs": true, + // This is necessary for the automatic typing of the adapter config + "resolveJsonModule": true, + // If you want to disable the stricter type checks (not recommended), uncomment the following line + // "strict": false, + // And enable some of those features for more fine-grained control + "strictNullChecks": true, + // "strictPropertyInitialization": true, + // "strictBindCallApply": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + "useUnknownInCatchVariables": false, + "target": "ES2022", + "baseUrl": "./", + "allowSyntheticDefaultImports": true, + "checkJs": false, + "noEmit": false, + "outDir": "./build", + "sourceMap": true, + "sourceRoot": "./src", + "noImplicitAny": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "lib": ["es2018", "DOM"], + "jsx": "react" + }, + "include": ["./src/**/*.ts", "./src/**/*.d.ts", "./src/**/*.tsx", "./src/**/*.json", "./src/**/*.css"], + "exclude": ["**/node_modules", "**/dist"] +} diff --git a/frontend/vite.config.mjs b/frontend/vite.config.mjs new file mode 100644 index 0000000..0aebad3 --- /dev/null +++ b/frontend/vite.config.mjs @@ -0,0 +1,36 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig(() => { + return { + build: { + outDir: 'build', + }, + plugins: [react()], + resolve: { + mainFields: [], + }, + base: './', + server: { + port: 3000, + proxy: { + '/adapter': { + target: 'http://localhost:8081', + changeOrigin: true, + secure: false, + configure: (proxy, _options) => { + proxy.on('error', (err, _req, _res) => { + console.log('proxy error', err); + }); + proxy.on('proxyReq', (proxyReq, req, _res) => { + console.log('Sending Request to the Target:', req.method, req.url); + }); + proxy.on('proxyRes', (proxyRes, req, _res) => { + console.log('Received Response from the Target:', proxyRes.statusCode, req.url); + }); + }, + }, + }, + }, + }; +}); diff --git a/package.json b/package.json index ce32cb5..0f4da76 100644 --- a/package.json +++ b/package.json @@ -1,38 +1,38 @@ { - "version": "3.2.3", - "name": "@iobroker/repochecker", - "dependencies": { - "axios": "^1.7.9", - "compare-versions": "^6.1.1", - "image-size": "^1.2.0", - "unzipper": "^0.12.3", - "json5": "^2.2.3" - }, - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "@alcalzone/release-script": "^3.8.0", - "eslint": "^8.57.0" - }, - "files": [ - "lib/", - "LICENSE", - "index.js", - "doc/issues.json" - ], - "engines": { - "node": ">=16" - }, - "main": "index.js", - "bin": "index.js", - "scripts": { - "prepublishOnly": "node doc/readme.js", - "start": "node index.js", - "lint": "eslint", - "release": "release-script", - "release-patch": "release-script patch --yes --no-update-lockfile", - "release-minor": "release-script minor --yes --no-update-lockfile", - "release-major": "release-script major --yes --no-update-lockfile" - } + "version": "3.2.3", + "name": "@iobroker/repochecker", + "dependencies": { + "axios": "^1.7.9", + "compare-versions": "^6.1.1", + "image-size": "^1.2.0", + "unzipper": "^0.12.3", + "json5": "^2.2.3" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@alcalzone/release-script": "^3.8.0", + "@iobroker/eslint-config": "^1.0.0" + }, + "files": [ + "lib/", + "LICENSE", + "index.js", + "doc/issues.json" + ], + "engines": { + "node": ">=16" + }, + "main": "index.js", + "bin": "index.js", + "scripts": { + "prepublishOnly": "node doc/readme.js", + "start": "node index.js", + "lint": "eslint", + "release": "release-script", + "release-patch": "release-script patch --yes --no-update-lockfile", + "release-minor": "release-script minor --yes --no-update-lockfile", + "release-major": "release-script major --yes --no-update-lockfile" + } } diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 0000000..2f00708 --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,3 @@ +import prettierConfig from '@iobroker/eslint-config/prettier.config.mjs'; + +export default prettierConfig;