From bccb03d6fbcb8f34dd5c55edb13cca26edf3d961 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Wed, 14 May 2025 03:39:35 -0500 Subject: [PATCH] Update eslint config and fix errors --- ui/.eslintrc.cjs | 66 ----- ui/eslint.config.cjs | 93 +++++++ ui/package-lock.json | 234 +++++++----------- ui/package.json | 4 + ui/src/components/Combobox.tsx | 2 + ui/src/components/ConfirmDialog.tsx | 1 + ui/src/components/MacroForm.tsx | 3 +- ui/src/components/VideoOverlay.tsx | 3 +- ui/src/components/WebRTCVideo.tsx | 4 +- ui/src/hooks/stores.ts | 1 + ui/src/main.tsx | 13 +- .../devices.$id.settings.access._index.tsx | 4 +- ui/src/routes/devices.$id.settings.macros.tsx | 2 +- .../routes/devices.$id.settings.network.tsx | 29 +-- ui/src/routes/devices.$id.settings.tsx | 11 +- ui/src/routes/devices.tsx | 14 +- 16 files changed, 232 insertions(+), 252 deletions(-) delete mode 100644 ui/.eslintrc.cjs create mode 100644 ui/eslint.config.cjs diff --git a/ui/.eslintrc.cjs b/ui/.eslintrc.cjs deleted file mode 100644 index 568fbd9..0000000 --- a/ui/.eslintrc.cjs +++ /dev/null @@ -1,66 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/stylistic", - "plugin:react-hooks/recommended", - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:import/recommended", - "prettier", - ], - ignorePatterns: ["dist", ".eslintrc.cjs", "tailwind.config.js", "postcss.config.js"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - project: ["./tsconfig.json", "./tsconfig.node.json"], - tsconfigRootDir: __dirname, - }, - rules: { - "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], - "import/order": [ - "error", - { - /** - * @description - * - * This keeps imports separate from one another, ensuring that imports are separated - * by their relative groups. As you move through the groups, imports become closer - * to the current file. - * - * @example - * ``` - * import fs from 'fs'; - * - * import package from 'npm-package'; - * - * import xyz from '~/project-file'; - * - * import index from '../'; - * - * import sibling from './foo'; - * ``` - */ - groups: ["builtin", "external", "internal", "parent", "sibling"], - "newlines-between": "always", - }, - ], - }, - settings: { - "import/resolver": { - alias: { - map: [ - ["@components", "./src/components"], - ["@routes", "./src/routes"], - ["@assets", "./src/assets"], - ["@", "./src"], - ], - extensions: [".ts", ".tsx", ".js", ".jsx", ".json"], - }, - }, - }, -}; diff --git a/ui/eslint.config.cjs b/ui/eslint.config.cjs new file mode 100644 index 0000000..a6c0c1f --- /dev/null +++ b/ui/eslint.config.cjs @@ -0,0 +1,93 @@ +const { + defineConfig, + globalIgnores, +} = require("eslint/config"); + +const globals = require("globals"); + +const { + fixupConfigRules, +} = require("@eslint/compat"); + +const tsParser = require("@typescript-eslint/parser"); +const reactRefresh = require("eslint-plugin-react-refresh"); +const js = require("@eslint/js"); + +const { + FlatCompat, +} = require("@eslint/eslintrc"); + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +module.exports = defineConfig([{ + languageOptions: { + globals: { + ...globals.browser, + }, + + parser: tsParser, + ecmaVersion: "latest", + sourceType: "module", + + parserOptions: { + project: ["./tsconfig.json", "./tsconfig.node.json"], + tsconfigRootDir: __dirname, + ecmaFeatures: { + jsx: true + } + }, + }, + + extends: fixupConfigRules(compat.extends( + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/stylistic", + "plugin:react-hooks/recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:import/recommended", + "prettier", + )), + + plugins: { + "react-refresh": reactRefresh, + }, + + rules: { + "react-refresh/only-export-components": ["warn", { + allowConstantExport: true, + }], + + "import/order": ["error", { + groups: ["builtin", "external", "internal", "parent", "sibling"], + "newlines-between": "always", + }], + }, + + settings: { + "react": { + "version": "detect" + }, + "import/resolver": { + alias: { + map: [ + ["@components", "./src/components"], + ["@routes", "./src/routes"], + ["@assets", "./src/assets"], + ["@", "./src"], + ], + + extensions: [".ts", ".tsx", ".js", ".jsx", ".json"], + }, + }, + }, +}, globalIgnores([ + "**/dist", + "**/.eslintrc.cjs", + "**/tailwind.config.js", + "**/postcss.config.js", +])]); diff --git a/ui/package-lock.json b/ui/package-lock.json index 4949859..7817253 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -41,6 +41,9 @@ "zustand": "^4.5.2" }, "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.26.0", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.6", "@tailwindcss/typography": "^0.5.16", @@ -59,6 +62,7 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.1.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", @@ -534,6 +538,24 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/compat": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.9.tgz", + "integrity": "sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, "node_modules/@eslint/config-array": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", @@ -548,28 +570,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/config-helpers": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", @@ -614,35 +614,16 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "license": "MIT", "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "node": ">=18" }, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@eslint/js": { @@ -2053,6 +2034,16 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { "version": "8.32.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", @@ -2161,6 +2152,32 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { "version": "8.32.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", @@ -2607,13 +2624,13 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -3648,16 +3665,6 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -3667,18 +3674,6 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-import/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3744,30 +3739,6 @@ "eslint": ">=8.40" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -3824,16 +3795,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", @@ -3846,27 +3807,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -4239,9 +4179,9 @@ } }, "node_modules/framer-motion": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.11.0.tgz", - "integrity": "sha512-BaBPmkhaC2l0n619Kt1nQaxSdUdyyz5V1Z7EKJ1CcraOTZitgVx0RTbL8lmg2XesaFi6o8MPBIhkWDIvzDpGaQ==", + "version": "12.11.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.11.1.tgz", + "integrity": "sha512-k8SlrjoDesLTBklizQ2BSW7Pb99cHDoZe51Tff9ZjmWHQ/N5DLly949veBnwNfeH4d0E/h02RKuul/6D+5u9Bg==", "license": "MIT", "dependencies": { "motion-dom": "^12.11.0", @@ -4393,9 +4333,10 @@ } }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -4578,10 +4519,9 @@ } }, "node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", - "dev": true, + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -5532,19 +5472,15 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { diff --git a/ui/package.json b/ui/package.json index 74ae21d..e1e8188 100644 --- a/ui/package.json +++ b/ui/package.json @@ -52,6 +52,9 @@ "zustand": "^4.5.2" }, "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.26.0", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.6", "@tailwindcss/typography": "^0.5.16", @@ -70,6 +73,7 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.1.0", "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", diff --git a/ui/src/components/Combobox.tsx b/ui/src/components/Combobox.tsx index 45528c9..ba64cb7 100644 --- a/ui/src/components/Combobox.tsx +++ b/ui/src/components/Combobox.tsx @@ -1,7 +1,9 @@ import { useRef } from "react"; import clsx from "clsx"; import { Combobox as HeadlessCombobox, ComboboxInput, ComboboxOption, ComboboxOptions } from "@headlessui/react"; + import { cva } from "@/cva.config"; + import Card from "./Card"; export interface ComboboxOption { diff --git a/ui/src/components/ConfirmDialog.tsx b/ui/src/components/ConfirmDialog.tsx index 57391e2..729461b 100644 --- a/ui/src/components/ConfirmDialog.tsx +++ b/ui/src/components/ConfirmDialog.tsx @@ -1,4 +1,5 @@ import { ExclamationTriangleIcon, CheckCircleIcon, InformationCircleIcon } from "@heroicons/react/24/outline"; + import { cx } from "@/cva.config"; import { Button } from "@/components/Button"; import Modal from "@/components/Modal"; diff --git a/ui/src/components/MacroForm.tsx b/ui/src/components/MacroForm.tsx index 135817c..342b5c2 100644 --- a/ui/src/components/MacroForm.tsx +++ b/ui/src/components/MacroForm.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; - import { LuPlus } from "react-icons/lu"; import { KeySequence } from "@/hooks/stores"; @@ -7,8 +6,8 @@ import { Button } from "@/components/Button"; import { InputFieldWithLabel, FieldError } from "@/components/InputField"; import Fieldset from "@/components/Fieldset"; import { MacroStepCard } from "@/components/MacroStepCard"; -import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros"; import FieldLabel from "@/components/FieldLabel"; +import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros"; interface ValidationErrors { name?: string; diff --git a/ui/src/components/VideoOverlay.tsx b/ui/src/components/VideoOverlay.tsx index 4fa1b69..5adbbbb 100644 --- a/ui/src/components/VideoOverlay.tsx +++ b/ui/src/components/VideoOverlay.tsx @@ -3,11 +3,12 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/solid"; import { ArrowPathIcon, ArrowRightIcon } from "@heroicons/react/16/solid"; import { motion, AnimatePresence } from "framer-motion"; import { LuPlay } from "react-icons/lu"; +import { BsMouseFill } from "react-icons/bs"; import { Button, LinkButton } from "@components/Button"; import LoadingSpinner from "@components/LoadingSpinner"; import Card, { GridCard } from "@components/Card"; -import { BsMouseFill } from "react-icons/bs"; + interface OverlayContentProps { children: React.ReactNode; diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index b2cb430..8311b6f 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useResizeObserver } from "usehooks-ts"; import { useDeviceSettingsStore, @@ -10,7 +11,6 @@ import { useVideoStore, } from "@/hooks/stores"; import { keys, modifiers } from "@/keyboardMappings"; -import { useResizeObserver } from "usehooks-ts"; import { cx } from "@/cva.config"; import VirtualKeyboard from "@components/VirtualKeyboard"; import Actionbar from "@components/ActionBar"; @@ -151,7 +151,7 @@ export default function WebRTCVideo() { const isKeyboardLockGranted = await checkNavigatorPermissions("keyboard-lock"); if (isKeyboardLockGranted) { if ("keyboard" in navigator) { - // @ts-ignore + // @ts-expect-error because ignore error isn't good enough await navigator.keyboard.lock(); } } diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index c100d88..f491331 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -1,5 +1,6 @@ import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; + import { MAX_STEPS_PER_MACRO, MAX_TOTAL_MACROS, MAX_KEYS_PER_STEP } from "@/constants/macros"; // Define the JsonRpc types for better type checking diff --git a/ui/src/main.tsx b/ui/src/main.tsx index f8e3b84..cbd5e25 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -17,7 +17,7 @@ import AdoptRoute from "@routes/adopt"; import SignupRoute from "@routes/signup"; import LoginRoute from "@routes/login"; import SetupRoute from "@routes/devices.$id.setup"; -import DevicesRoute, { loader as DeviceListLoader } from "@routes/devices"; +import DevicesRoute from "@routes/devices"; import DeviceRoute, { LocalDevice } from "@routes/devices.$id"; import Card from "@components/Card"; import DevicesAlreadyAdopted from "@routes/devices.already-adopted"; @@ -36,7 +36,7 @@ import SettingsKeyboardMouseRoute from "./routes/devices.$id.settings.mouse"; import api from "./api"; import * as SettingsIndexRoute from "./routes/devices.$id.settings._index"; import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced"; -import * as SettingsAccessIndexRoute from "./routes/devices.$id.settings.access._index"; +import SettingsAccessIndexRoute from "./routes/devices.$id.settings.access._index"; import SettingsHardwareRoute from "./routes/devices.$id.settings.hardware"; import SettingsVideoRoute from "./routes/devices.$id.settings.video"; import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance"; @@ -166,7 +166,7 @@ if (isOnDevice) { children: [ { index: true, - element: , + element: , loader: SettingsAccessIndexRoute.loader, }, { @@ -291,7 +291,7 @@ if (isOnDevice) { children: [ { index: true, - element: , + element: , loader: SettingsAccessIndexRoute.loader, }, { @@ -341,7 +341,10 @@ if (isOnDevice) { loader: DeviceIdRename.loader, action: DeviceIdRename.action, }, - { path: "devices", element: , loader: DeviceListLoader }, + { + path: "devices", + element: , + loader: DevicesRoute.loader }, ], }, ], diff --git a/ui/src/routes/devices.$id.settings.access._index.tsx b/ui/src/routes/devices.$id.settings.access._index.tsx index d8eebf9..f21927a 100644 --- a/ui/src/routes/devices.$id.settings.access._index.tsx +++ b/ui/src/routes/devices.$id.settings.access._index.tsx @@ -26,7 +26,7 @@ export interface TLSState { privateKey?: string; } -export const loader = async () => { +const loader = async () => { if (isOnDevice) { const status = await api .GET(`${DEVICE_API}/device`) @@ -468,3 +468,5 @@ export default function SettingsAccessIndexRoute() { ); } + +SettingsAccessIndexRoute.loader = loader; \ No newline at end of file diff --git a/ui/src/routes/devices.$id.settings.macros.tsx b/ui/src/routes/devices.$id.settings.macros.tsx index 140e662..614d55c 100644 --- a/ui/src/routes/devices.$id.settings.macros.tsx +++ b/ui/src/routes/devices.$id.settings.macros.tsx @@ -250,7 +250,7 @@ export default function SettingsMacrosRoute() { isConfirming={actionLoadingId === macroToDelete?.id} /> - ), [macros, actionLoadingId, showDeleteConfirm, macroToDelete, handleDeleteMacro]); + ), [macros, actionLoadingId, showDeleteConfirm, macroToDelete, handleDeleteMacro, handleDuplicateMacro, handleMoveMacro, navigate]); return (
diff --git a/ui/src/routes/devices.$id.settings.network.tsx b/ui/src/routes/devices.$id.settings.network.tsx index 59d52ef..82921eb 100644 --- a/ui/src/routes/devices.$id.settings.network.tsx +++ b/ui/src/routes/devices.$id.settings.network.tsx @@ -1,18 +1,19 @@ import { useCallback, useEffect, useState } from "react"; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +import { IPv4Mode, IPv6Mode, LLDPMode, mDNSMode, NetworkSettings, NetworkState, TimeSyncMode, useNetworkStateStore } from "@/hooks/stores"; +import { useJsonRpc } from "@/hooks/useJsonRpc"; +import { Button } from "@components/Button"; +import { GridCard } from "@components/Card"; +import InputField from "@components/InputField"; +import notifications from "@/notifications"; import { SelectMenuBasic } from "../components/SelectMenuBasic"; import { SettingsPageHeader } from "../components/SettingsPageheader"; -import { IPv4Mode, IPv6Mode, LLDPMode, mDNSMode, NetworkSettings, NetworkState, TimeSyncMode, useNetworkStateStore } from "@/hooks/stores"; -import { useJsonRpc } from "@/hooks/useJsonRpc"; -import notifications from "@/notifications"; -import { Button } from "@components/Button"; -import { GridCard } from "@components/Card"; -import InputField from "@components/InputField"; -import { SettingsItem } from "./devices.$id.settings"; -import dayjs from 'dayjs'; -import relativeTime from 'dayjs/plugin/relativeTime'; +import { SettingsItem } from "./devices.$id.settings"; dayjs.extend(relativeTime); @@ -28,10 +29,6 @@ const defaultNetworkSettings: NetworkSettings = { } export function LifeTimeLabel({ lifetime }: { lifetime: string }) { - if (lifetime == "") { - return N/A; - } - const [remaining, setRemaining] = useState(null); useEffect(() => { @@ -43,6 +40,10 @@ export function LifeTimeLabel({ lifetime }: { lifetime: string }) { return () => clearInterval(interval); }, [lifetime]); + if (lifetime == "") { + return N/A; + } + return <> {dayjs(lifetime).format()} {remaining && <> @@ -90,7 +91,7 @@ export default function SettingsNetworkRoute() { console.log(resp.result); setNetworkState(resp.result as NetworkState); }); - }, [send]); + }, [send, setNetworkState]); const handleRenewLease = useCallback(() => { send("renewDHCPLease", {}, resp => { diff --git a/ui/src/routes/devices.$id.settings.tsx b/ui/src/routes/devices.$id.settings.tsx index b75d1e1..641a64c 100644 --- a/ui/src/routes/devices.$id.settings.tsx +++ b/ui/src/routes/devices.$id.settings.tsx @@ -12,15 +12,16 @@ import { LuNetwork, } from "react-icons/lu"; import React, { useEffect, useRef, useState } from "react"; +import { useResizeObserver } from "usehooks-ts"; import Card from "@/components/Card"; +import { LinkButton } from "@/components/Button"; +import LoadingSpinner from "@/components/LoadingSpinner"; +import { useUiStore } from "@/hooks/stores"; +import useKeyboard from "@/hooks/useKeyboard"; -import { LinkButton } from "../components/Button"; import { cx } from "../cva.config"; -import { useUiStore } from "../hooks/stores"; -import useKeyboard from "../hooks/useKeyboard"; -import { useResizeObserver } from "usehooks-ts"; -import LoadingSpinner from "../components/LoadingSpinner"; + /* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */ export default function SettingsRoute() { diff --git a/ui/src/routes/devices.tsx b/ui/src/routes/devices.tsx index 391b62a..9090646 100644 --- a/ui/src/routes/devices.tsx +++ b/ui/src/routes/devices.tsx @@ -1,14 +1,14 @@ import { useLoaderData, useRevalidator } from "react-router-dom"; import { LuMonitorSmartphone } from "react-icons/lu"; import { ArrowRightIcon } from "@heroicons/react/16/solid"; +import { useInterval } from "usehooks-ts"; import DashboardNavbar from "@components/Header"; -import { LinkButton } from "@components/Button"; -import KvmCard from "@components/KvmCard"; -import { useInterval } from "usehooks-ts"; -import { checkAuth } from "@/main"; -import { User } from "@/hooks/stores"; import EmptyCard from "@components/EmptyCard"; +import KvmCard from "@components/KvmCard"; +import { LinkButton } from "@components/Button"; +import { User } from "@/hooks/stores"; +import { checkAuth } from "@/main"; import { CLOUD_API } from "@/ui.config"; interface LoaderData { @@ -16,7 +16,7 @@ interface LoaderData { user: User; } -export const loader = async () => { +const loader = async () => { const user = await checkAuth(); try { @@ -101,3 +101,5 @@ export default function DevicesRoute() {
); } + +DevicesRoute.loader = loader; \ No newline at end of file