mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
build(web): bump vite and cjs node api refactor (#1276)
* refactor: use ES module. To maintain compatibility with vite 6 and since that's where the web are heading too. Also moved some deps to devDeps, better optimized production builds. Changed some of the script command to match how others run or preview it, I think it was still using CRA. * chore: update-lock.yaml * refactor: since we are using ESM now, .cjs .ts required. Changed the file extensions and refactored the .eslintrc.cjs I think there was a lot of bloat from the previous version and removed most of them and keep it simple for now, we can always expand from here a clean slate. * refactor: added .node.json and refactored. * fix(build): APIClient.ts had few error. ESLint: Unexpected lexical declaration in case block.(no-case-declarations) and TS2554: Expected 0-1 arguments, but got 2 we passed the cause to the constructor which it only takes 1 argument so removed it instead, since it's already in the string "Offline". * fix(build): import never used. * fix(build): add the types for react-dom/client. * fix(build): use ESNext instead. * fix(build): hmm why are we missing the types for the import? Added @types/react-table. * chore(lint): fix lint warnings Don't use * for export. * chore(lint): missing deps. React Hook useEffect has a missing dependency: 'validateForm'. Either include it or remove the dependency array * chore(lint): fix import. * chore(lint): fix import. * chore(lint): fix react hook. error React Hook "useMutation" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks * chore(lint): value never used. 52:10 error '_regexPattern' is assigned a value but never used * chore(lint): add missing dependency to useEffect * chore(lint): fix imports. * chore(lint): add deps to array. * chore(lint): error Unexpected lexical declaration in case block no-case-declarations * chore(lint): remove any and add types. I am not sure about type CompleteFilterType I know it's being used for JSON so might need to use any?? dunno just test it and see if works. * chore(lint): react-hooks/exhaustive-deps * chore(lint): react-hooks/exhaustive-deps * chore(lint): use type guard instead of any. * chore(lint): react-hooks/exhaustive-deps * Revert "chore(lint): remove any and add types." This reverts commit 7b9168fe7826d63cb00e44df8e15fbde49b59174. * chore(web): ignore sourcemap warnings * chore(web): update vite to 5.0.4 * chore: add the new script `pnpm dev` to start the dev env. * chore: add the curly braces. more info: https://eslint.org/docs/latest/rules/no-case-declarations * chore: remove the extra spaces * chore: remove the extra spaces * chore: add the curly braces. * chore: add curly braces * remove text-shadow property and comment * revert switch case braces for Actions.tsx --------- Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
This commit is contained in:
parent
a89a1a55d9
commit
92646dacc8
36 changed files with 1100 additions and 1029 deletions
|
@ -22,9 +22,9 @@ test
|
||||||
#!web/yarn.lock
|
#!web/yarn.lock
|
||||||
#!web/.yarnrc.yml
|
#!web/.yarnrc.yml
|
||||||
#!web/.yarn/releases
|
#!web/.yarn/releases
|
||||||
#!web/.eslintrc.js
|
#!web/.eslintrc.cjs
|
||||||
#!web/postcss.config.js
|
#!web/postcss.config.cts
|
||||||
#!web/tailwind.config.js
|
#!web/tailwind.config.ts
|
||||||
#!web/tsconfig.json
|
#!web/tsconfig.json
|
||||||
#!web/index.html
|
#!web/index.html
|
||||||
#!web/vite.config.ts
|
#!web/vite.config.ts
|
||||||
|
|
18
web/.eslintrc.cjs
Normal file
18
web/.eslintrc.cjs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,87 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: "@typescript-eslint/parser",
|
|
||||||
plugins: [
|
|
||||||
"@typescript-eslint",
|
|
||||||
],
|
|
||||||
// If we ever decide on a code-style, I'll leave this here.
|
|
||||||
//extends: [
|
|
||||||
// "airbnb",
|
|
||||||
// "airbnb/hooks",
|
|
||||||
// "airbnb-typescript",
|
|
||||||
//],
|
|
||||||
rules: {
|
|
||||||
// Turn off pesky "react not in scope" error while
|
|
||||||
// we transition to proper ESLint support
|
|
||||||
"react/react-in-jsx-scope": "off",
|
|
||||||
// Add a UNIX-style linebreak at the end of each file
|
|
||||||
"linebreak-style": ["error", "unix"],
|
|
||||||
// Allow only double quotes and backticks
|
|
||||||
quotes: ["error", "double"],
|
|
||||||
// Warn if a line isn't indented with a multiple of 2
|
|
||||||
indent: ["warn", 2, { "SwitchCase": 0 }],
|
|
||||||
// Don't enforce any particular brace style
|
|
||||||
curly: "off",
|
|
||||||
// Allow only vars starting with _ to be ununsed vars
|
|
||||||
"no-unused-vars": ["warn", {
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"caughtErrorsIgnorePattern": "^_",
|
|
||||||
"ignoreRestSiblings": true
|
|
||||||
}],
|
|
||||||
// Let's keep these off for now and
|
|
||||||
// maybe turn these back on sometime in the future
|
|
||||||
"import/prefer-default-export": "off",
|
|
||||||
"react/function-component-definition": "off",
|
|
||||||
"nonblock-statement-body-position": ["warn", "below"]
|
|
||||||
},
|
|
||||||
// Conditionally run the following configuration only for TS files.
|
|
||||||
// Otherwise, this will create inter-op problems with JS files.
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
// Run only .ts and .tsx files
|
|
||||||
files: ["*.ts", "*.tsx"],
|
|
||||||
// Define the @typescript-eslint plugin schemas
|
|
||||||
extends: [
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
// Don't require strict type-checking for now, since we have too many
|
|
||||||
// dubious statements literred in the code.
|
|
||||||
//"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
// project: "tsconfig.json",
|
|
||||||
// This is needed so we can always point to the tsconfig.json
|
|
||||||
// file relative to the current .eslintrc.js file.
|
|
||||||
// Generally, a problem occurrs when "npm run lint"
|
|
||||||
// gets ran from another directory. This fixes it.
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
sourceType: "module",
|
|
||||||
},
|
|
||||||
// Override JS rules and apply @typescript-eslint rules
|
|
||||||
// as they might interfere with eachother.
|
|
||||||
rules: {
|
|
||||||
quotes: "off",
|
|
||||||
"@typescript-eslint/quotes": ["error", "double"],
|
|
||||||
semi: "off",
|
|
||||||
"@typescript-eslint/semi": ["warn", "always"],
|
|
||||||
indent: ["warn", 2, { "SwitchCase": 0 }],
|
|
||||||
"@typescript-eslint/indent": "off",
|
|
||||||
"@typescript-eslint/comma-dangle": "warn",
|
|
||||||
"keyword-spacing": "off",
|
|
||||||
"@typescript-eslint/keyword-spacing": ["error"],
|
|
||||||
"object-curly-spacing": "off",
|
|
||||||
"@typescript-eslint/object-curly-spacing": ["warn", "always"],
|
|
||||||
// Allow only vars starting with _ to be ununsed vars
|
|
||||||
"@typescript-eslint/no-unused-vars": ["warn", {
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"caughtErrorsIgnorePattern": "^_",
|
|
||||||
"ignoreRestSiblings": true
|
|
||||||
}],
|
|
||||||
// We have quite some "Unexpected any. Specify a different type" warnings.
|
|
||||||
// This disables these warnings since they are false positives afaict.
|
|
||||||
"@typescript-eslint/no-explicit-any": "off"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
|
@ -6,7 +6,7 @@ This project uses React built with Vite.
|
||||||
|
|
||||||
In the project directory, you can run:
|
In the project directory, you can run:
|
||||||
|
|
||||||
### `pnpm start`
|
### `pnpm dev`
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
Runs the app in the development mode.\
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "web",
|
"name": "web",
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": ".",
|
"homepage": ".",
|
||||||
"packageManager": "pnpm@8.10.5",
|
"packageManager": "pnpm@8.10.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"serve": "vite preview",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --color",
|
||||||
"lint": "eslint src/ --ext .js,.jsx,.ts,.tsx --color",
|
"preview": "vite preview",
|
||||||
"lint:watch": "pnpm run lint -- --watch"
|
"lint:watch": "pnpm run lint -- --watch"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
|
@ -35,23 +36,9 @@
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tanstack/react-query": "^4.36.1",
|
"@tanstack/react-query": "^4.36.1",
|
||||||
"@tanstack/react-query-devtools": "^4.36.1",
|
"@tanstack/react-query-devtools": "^4.36.1",
|
||||||
"@types/node": "^20.9.1",
|
|
||||||
"@types/react": "^18.2.37",
|
|
||||||
"@types/react-dom": "^18.2.15",
|
|
||||||
"@types/react-portal": "^4.0.6",
|
|
||||||
"@types/react-router-dom": "^5.3.3",
|
|
||||||
"@types/react-table": "^7.7.18",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
|
||||||
"@typescript-eslint/parser": "^6.11.0",
|
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"eslint": "^8.54.0",
|
|
||||||
"eslint-plugin-import": "^2.29.0",
|
|
||||||
"eslint-plugin-react": "^7.33.2",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-watch": "^8.0.0",
|
|
||||||
"formik": "^2.4.5",
|
"formik": "^2.4.5",
|
||||||
"http-proxy-middleware": "^2.0.6",
|
"http-proxy-middleware": "^2.0.6",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
|
@ -72,12 +59,29 @@
|
||||||
"stacktracey": "^2.1.8",
|
"stacktracey": "^2.1.8",
|
||||||
"tailwind-lerp-colors": "1.2.1",
|
"tailwind-lerp-colors": "1.2.1",
|
||||||
"tailwindcss": "^3.3.5",
|
"tailwindcss": "^3.3.5",
|
||||||
"typescript": "^5.2.2",
|
|
||||||
"vite": "4.5.0",
|
|
||||||
"vite-plugin-pwa": "^0.16.7",
|
|
||||||
"vite-plugin-svgr": "^4.2.0",
|
|
||||||
"workbox-window": "^7.0.0",
|
"workbox-window": "^7.0.0",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zod-formik-adapter": "^1.2.0"
|
"zod-formik-adapter": "^1.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.9.1",
|
||||||
|
"@types/react": "^18.2.37",
|
||||||
|
"@types/react-dom": "^18.2.15",
|
||||||
|
"@types/react-portal": "^4.0.6",
|
||||||
|
"@types/react-table": "^7.7.18",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||||
|
"@typescript-eslint/parser": "^6.11.0",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
|
"eslint": "^8.54.0",
|
||||||
|
"eslint-plugin-import": "^2.29.0",
|
||||||
|
"eslint-plugin-react": "^7.33.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.4",
|
||||||
|
"eslint-watch": "^8.0.0",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.0.4",
|
||||||
|
"vite-plugin-pwa": "^0.16.7",
|
||||||
|
"vite-plugin-svgr": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1673
web/pnpm-lock.yaml
generated
1673
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -82,25 +82,29 @@ export async function HttpClient<T = unknown>(
|
||||||
const response = await window.fetch(`${baseUrl()}${endpoint}`, init);
|
const response = await window.fetch(`${baseUrl()}${endpoint}`, init);
|
||||||
|
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 204:
|
case 204: {
|
||||||
// 204 contains no data, but indicates success
|
// 204 contains no data, but indicates success
|
||||||
return Promise.resolve<T>({} as T);
|
return Promise.resolve<T>({} as T);
|
||||||
case 401:
|
}
|
||||||
|
case 401: {
|
||||||
// Remove auth info from localStorage
|
// Remove auth info from localStorage
|
||||||
AuthContext.reset();
|
AuthContext.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Show an error toast to notify the user what occurred
|
// Show an error toast to notify the user what occurred
|
||||||
return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`));
|
return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`));
|
||||||
case 404:
|
case 404: {
|
||||||
return Promise.reject(new Error(`[404] Not found: "${endpoint}"`));
|
return Promise.reject(new Error(`[404] Not found: "${endpoint}"`));
|
||||||
case 500:
|
}
|
||||||
|
case 500: {
|
||||||
const health = await window.fetch(`${baseUrl()}api/healthz/liveness`);
|
const health = await window.fetch(`${baseUrl()}api/healthz/liveness`);
|
||||||
if (!health.ok) {
|
if (!health.ok) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(`[500] Offline (Internal server error): "${endpoint}"`, { cause: "OFFLINE" })
|
new Error(`[500] Offline (Internal server error): "${endpoint}"`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
interface IconProps {
|
interface IconProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
|
@ -20,11 +20,13 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
|
||||||
|
|
||||||
const parseTitle = () => {
|
const parseTitle = () => {
|
||||||
switch (error?.cause) {
|
switch (error?.cause) {
|
||||||
case "OFFLINE":
|
case "OFFLINE": {
|
||||||
return "Connection to Autobrr failed! Check the application state and verify your connectivity.";
|
return "Connection to Autobrr failed! Check the application state and verify your connectivity.";
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
return "We caught an unrecoverable error!";
|
return "We caught an unrecoverable error!";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./ErrorPage";
|
export { ErrorPage } from './ErrorPage';
|
||||||
export * from "./Warning";
|
export { WarningAlert } from "./Warning";
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./Buttons";
|
export { Button, PageButton } from "./Buttons";
|
||||||
export * from "./Cells";
|
export { AgeCell, IndexerCell, TitleCell, ReleaseStatusCell } from "./Cells";
|
|
@ -10,7 +10,7 @@ interface DebugProps {
|
||||||
values: unknown;
|
values: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEBUG: FC<DebugProps> = ({ values }) => {
|
export const DEBUG: FC<DebugProps> = ({ values }) => {
|
||||||
const settings = SettingsContext.useValue();
|
const settings = SettingsContext.useValue();
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== "development" || !settings.debug) {
|
if (process.env.NODE_ENV !== "development" || !settings.debug) {
|
||||||
|
@ -23,5 +23,3 @@ const DEBUG: FC<DebugProps> = ({ values }) => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DEBUG;
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ export const RegexField = ({
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
validateForm();
|
validateForm();
|
||||||
}
|
}
|
||||||
}, [useRegex]);
|
}, [useRegex, validateForm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -317,7 +317,7 @@ export const RegexTextAreaField = ({
|
||||||
if (useRegex) {
|
if (useRegex) {
|
||||||
validateForm();
|
validateForm();
|
||||||
}
|
}
|
||||||
}, [useRegex]);
|
}, [useRegex, validateForm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { Form, Formik } from "formik";
|
import { Form, Formik } from "formik";
|
||||||
import type { FormikValues, FormikProps } from "formik";
|
import type { FormikValues, FormikProps } from "formik";
|
||||||
|
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import { useToggle } from "@hooks/hooks";
|
import { useToggle } from "@hooks/hooks";
|
||||||
import { DeleteModal } from "@components/modals";
|
import { DeleteModal } from "@components/modals";
|
||||||
import { classNames } from "@utils";
|
import { classNames } from "@utils";
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { APIClient } from "@api/APIClient";
|
import { APIClient } from "@api/APIClient";
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
import { filterKeys } from "@screens/filters/List";
|
import { filterKeys } from "@screens/filters/List";
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import type { FieldProps } from "formik";
|
||||||
import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
|
import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
|
||||||
|
|
||||||
import { APIClient } from "@api/APIClient";
|
import { APIClient } from "@api/APIClient";
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
import { apiKeys } from "@screens/settings/Api";
|
import { apiKeys } from "@screens/settings/Api";
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Form, Formik, useFormikContext } from "formik";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
|
||||||
import { classNames, sleep } from "@utils";
|
import { classNames, sleep } from "@utils";
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import { APIClient } from "@api/APIClient";
|
import { APIClient } from "@api/APIClient";
|
||||||
import { DownloadClientTypeOptions, DownloadRuleConditionOptions } from "@domain/constants";
|
import { DownloadClientTypeOptions, DownloadRuleConditionOptions } from "@domain/constants";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { XMarkIcon } from "@heroicons/react/24/solid";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
import { classNames, sleep } from "@utils";
|
import { classNames, sleep } from "@utils";
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import { APIClient } from "@api/APIClient";
|
import { APIClient } from "@api/APIClient";
|
||||||
import { SlideOver } from "@components/panels";
|
import { SlideOver } from "@components/panels";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
|
@ -35,7 +35,7 @@ function validateField(s: IndexerSetting) {
|
||||||
return "Default value, please edit";
|
return "Default value, please edit";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !!value ? undefined : "Required";
|
return value ? undefined : "Required";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
|
|
||||||
{ind.irc.settings.map((f: IndexerSetting, idx: number) => {
|
{ind.irc.settings.map((f: IndexerSetting, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return (
|
return (
|
||||||
<TextFieldWide
|
<TextFieldWide
|
||||||
key={idx}
|
key={idx}
|
||||||
|
@ -76,12 +76,14 @@ const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
if (f.name === "invite_command") {
|
if (f.name === "invite_command") {
|
||||||
return <PasswordFieldWide defaultVisible name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
return <PasswordFieldWide defaultVisible name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
||||||
}
|
}
|
||||||
return <PasswordFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
return <PasswordFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,11 +110,13 @@ const TorznabFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
|
|
||||||
{ind.torznab.settings.map((f: IndexerSetting, idx: number) => {
|
{ind.torznab.settings.map((f: IndexerSetting, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
@ -147,11 +151,13 @@ const NewznabFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
|
|
||||||
{ind.newznab.settings.map((f: IndexerSetting, idx: number) => {
|
{ind.newznab.settings.map((f: IndexerSetting, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -178,11 +184,13 @@ const RSSFeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
|
|
||||||
{ind.rss.settings.map((f: IndexerSetting, idx: number) => {
|
{ind.rss.settings.map((f: IndexerSetting, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
return <TextFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />;
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
return <PasswordFieldWide name={`feed.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultValue={f.default} validate={validateField(f)} />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
@ -206,11 +214,12 @@ const SettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
<div key="opt">
|
<div key="opt">
|
||||||
{ind && ind.settings && ind.settings.map((f, idx: number) => {
|
{ind && ind.settings && ind.settings.map((f, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return (
|
return (
|
||||||
<TextFieldWide name={`settings.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />
|
<TextFieldWide name={`settings.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} autoComplete="off" validate={validateField(f)} />
|
||||||
);
|
);
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
return (
|
return (
|
||||||
<PasswordFieldWide
|
<PasswordFieldWide
|
||||||
name={`settings.${f.name}`}
|
name={`settings.${f.name}`}
|
||||||
|
@ -229,6 +238,7 @@ const SettingFields = (ind: IndexerDefinition, indexer: string) => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
<div hidden={true}>
|
<div hidden={true}>
|
||||||
|
@ -601,10 +611,6 @@ function TestApiButton({ values, show }: TestApiButtonProps) {
|
||||||
const [isSuccessfulTest, setIsSuccessfulTest] = useState(false);
|
const [isSuccessfulTest, setIsSuccessfulTest] = useState(false);
|
||||||
const [isErrorTest, setIsErrorTest] = useState(false);
|
const [isErrorTest, setIsErrorTest] = useState(false);
|
||||||
|
|
||||||
if (!show) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const testApiMutation = useMutation({
|
const testApiMutation = useMutation({
|
||||||
mutationFn: (req: IndexerTestApiReq) => APIClient.indexers.testApi(req),
|
mutationFn: (req: IndexerTestApiReq) => APIClient.indexers.testApi(req),
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
|
@ -638,6 +644,10 @@ function TestApiButton({ values, show }: TestApiButtonProps) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const testApi = () => {
|
const testApi = () => {
|
||||||
|
if (!show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const req: IndexerTestApiReq = {
|
const req: IndexerTestApiReq = {
|
||||||
id: values.id,
|
id: values.id,
|
||||||
api_key: values.settings.api_key
|
api_key: values.settings.api_key
|
||||||
|
@ -650,6 +660,9 @@ function TestApiButton({ values, show }: TestApiButtonProps) {
|
||||||
testApiMutation.mutate(req);
|
testApiMutation.mutate(req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
@ -761,11 +774,12 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
|
||||||
<div key="opt">
|
<div key="opt">
|
||||||
{settings.map((f: IndexerSetting, idx: number) => {
|
{settings.map((f: IndexerSetting, idx: number) => {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "text":
|
case "text": {
|
||||||
return (
|
return (
|
||||||
<TextFieldWide name={`settings.${f.name}`} label={f.label} key={idx} help={f.help} />
|
<TextFieldWide name={`settings.${f.name}`} label={f.label} key={idx} help={f.help} />
|
||||||
);
|
);
|
||||||
case "secret":
|
}
|
||||||
|
case "secret": {
|
||||||
return (
|
return (
|
||||||
<PasswordFieldWide
|
<PasswordFieldWide
|
||||||
key={idx}
|
key={idx}
|
||||||
|
@ -782,6 +796,7 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { toast } from "react-hot-toast";
|
||||||
import { APIClient } from "@api/APIClient";
|
import { APIClient } from "@api/APIClient";
|
||||||
import { notificationKeys } from "@screens/settings/Notifications";
|
import { notificationKeys } from "@screens/settings/Notifications";
|
||||||
import { EventOptions, NotificationTypeOptions, SelectOption } from "@domain/constants";
|
import { EventOptions, NotificationTypeOptions, SelectOption } from "@domain/constants";
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import { SlideOver } from "@components/panels";
|
import { SlideOver } from "@components/panels";
|
||||||
import { ExternalLink } from "@components/ExternalLink";
|
import { ExternalLink } from "@components/ExternalLink";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
|
|
|
@ -24,7 +24,6 @@ window.Buffer = Buffer;
|
||||||
// Initializes auth and theme contexts
|
// Initializes auth and theme contexts
|
||||||
InitializeGlobalContext();
|
InitializeGlobalContext();
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
const root = createRoot(document.getElementById("root")!);
|
const root = createRoot(document.getElementById("root")!);
|
||||||
root.render(
|
root.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|
|
@ -49,7 +49,7 @@ export const Logs = () => {
|
||||||
|
|
||||||
const [logs, setLogs] = useState<LogEvent[]>([]);
|
const [logs, setLogs] = useState<LogEvent[]>([]);
|
||||||
const [searchFilter, setSearchFilter] = useState("");
|
const [searchFilter, setSearchFilter] = useState("");
|
||||||
const [_regexPattern, setRegexPattern] = useState<RegExp | null>(null);
|
const [, setRegexPattern] = useState<RegExp | null>(null);
|
||||||
const [filteredLogs, setFilteredLogs] = useState<LogEvent[]>([]);
|
const [filteredLogs, setFilteredLogs] = useState<LogEvent[]>([]);
|
||||||
const [isInvalidRegex, setIsInvalidRegex] = useState(false);
|
const [isInvalidRegex, setIsInvalidRegex] = useState(false);
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export const Logs = () => {
|
||||||
};
|
};
|
||||||
if (settings.scrollOnNewLog)
|
if (settings.scrollOnNewLog)
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [filteredLogs]);
|
}, [filteredLogs, settings.scrollOnNewLog]);
|
||||||
|
|
||||||
// Add a useEffect to clear logs div when settings.scrollOnNewLog changes to prevent duplicate entries.
|
// Add a useEffect to clear logs div when settings.scrollOnNewLog changes to prevent duplicate entries.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const Login = () => {
|
||||||
APIClient.auth.canOnboard()
|
APIClient.auth.canOnboard()
|
||||||
.then(() => navigate("/onboard"))
|
.then(() => navigate("/onboard"))
|
||||||
.catch(() => { /*don't log to console PAHLLEEEASSSE*/ });
|
.catch(() => { /*don't log to console PAHLLEEEASSSE*/ });
|
||||||
}, []);
|
}, [navigate]);
|
||||||
|
|
||||||
const loginMutation = useMutation({
|
const loginMutation = useMutation({
|
||||||
mutationFn: (data: LoginFormFields) => APIClient.auth.login(data.username, data.password),
|
mutationFn: (data: LoginFormFields) => APIClient.auth.login(data.username, data.password),
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { useToggle } from "@hooks/hooks";
|
||||||
import { classNames } from "@utils";
|
import { classNames } from "@utils";
|
||||||
import { DOWNLOAD_CLIENTS } from "@domain/constants";
|
import { DOWNLOAD_CLIENTS } from "@domain/constants";
|
||||||
|
|
||||||
import DEBUG from "@components/debug";
|
import { DEBUG } from "@components/debug";
|
||||||
import Toast from "@components/notifications/Toast";
|
import Toast from "@components/notifications/Toast";
|
||||||
import { DeleteModal } from "@components/modals";
|
import { DeleteModal } from "@components/modals";
|
||||||
import { SectionLoader } from "@components/SectionLoader";
|
import { SectionLoader } from "@components/SectionLoader";
|
||||||
|
@ -164,7 +164,7 @@ const FormErrorNotification = () => {
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}, [isSubmitting, isValid, isValidating]);
|
}, [errors, isSubmitting, isValid, isValidating]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
@ -289,7 +289,6 @@ export const FilterDetails = () => {
|
||||||
navigate("/filters");
|
navigate("/filters");
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
const id = parseInt(filterId!);
|
const id = parseInt(filterId!);
|
||||||
|
|
||||||
const { isLoading, data: filter } = useQuery({
|
const { isLoading, data: filter } = useQuery({
|
||||||
|
|
|
@ -64,21 +64,28 @@ type Actions =
|
||||||
|
|
||||||
const FilterListReducer = (state: FilterListState, action: Actions): FilterListState => {
|
const FilterListReducer = (state: FilterListState, action: Actions): FilterListState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType.INDEXER_FILTER_CHANGE:
|
case ActionType.INDEXER_FILTER_CHANGE: {
|
||||||
return { ...state, indexerFilter: action.payload };
|
return { ...state, indexerFilter: action.payload };
|
||||||
case ActionType.INDEXER_FILTER_RESET:
|
}
|
||||||
|
case ActionType.INDEXER_FILTER_RESET: {
|
||||||
return { ...state, indexerFilter: [] };
|
return { ...state, indexerFilter: [] };
|
||||||
case ActionType.SORT_ORDER_CHANGE:
|
}
|
||||||
|
case ActionType.SORT_ORDER_CHANGE: {
|
||||||
return { ...state, sortOrder: action.payload };
|
return { ...state, sortOrder: action.payload };
|
||||||
case ActionType.SORT_ORDER_RESET:
|
}
|
||||||
|
case ActionType.SORT_ORDER_RESET: {
|
||||||
return { ...state, sortOrder: "" };
|
return { ...state, sortOrder: "" };
|
||||||
case ActionType.STATUS_CHANGE:
|
}
|
||||||
|
case ActionType.STATUS_CHANGE: {
|
||||||
return { ...state, status: action.payload };
|
return { ...state, status: action.payload };
|
||||||
case ActionType.STATUS_RESET:
|
}
|
||||||
|
case ActionType.STATUS_RESET: {
|
||||||
return { ...state, status: "" };
|
return { ...state, status: "" };
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
throw new Error(`Unhandled action type: ${action}`);
|
throw new Error(`Unhandled action type: ${action}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Filters() {
|
export function Filters() {
|
||||||
|
|
|
@ -25,7 +25,7 @@ class ParserFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "log_score":
|
case "log_score": {
|
||||||
// In this case we need to set 2 properties in autobrr instead of only 1
|
// In this case we need to set 2 properties in autobrr instead of only 1
|
||||||
this.values["log"] = true;
|
this.values["log"] = true;
|
||||||
|
|
||||||
|
@ -35,16 +35,18 @@ class ParserFilter {
|
||||||
value = value.slice(0, delim);
|
value = value.slice(0, delim);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "max_downloads_unit":
|
}
|
||||||
|
case "max_downloads_unit": {
|
||||||
value = value.toUpperCase();
|
value = value.toUpperCase();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key in CONST.FILTER_FIELDS) {
|
if (key in CONST.FILTER_FIELDS) {
|
||||||
switch (CONST.FILTER_FIELDS[key]) {
|
switch (CONST.FILTER_FIELDS[key]) {
|
||||||
case "number":
|
case "number": {
|
||||||
const parsedNum = parseFloat(value);
|
const parsedNum = parseFloat(value);
|
||||||
this.values[key] = parsedNum;
|
this.values[key] = parsedNum;
|
||||||
|
|
||||||
|
@ -53,11 +55,12 @@ class ParserFilter {
|
||||||
`[Filter=${this.name}] Failed to convert field '${key}' to a number. Got value: '${value}'`
|
`[Filter=${this.name}] Failed to convert field '${key}' to a number. Got value: '${value}'`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "boolean":
|
}
|
||||||
|
case "boolean": {
|
||||||
this.values[key] = value.toLowerCase() === "true";
|
this.values[key] = value.toLowerCase() === "true";
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
this.values[key] = value;
|
this.values[key] = value;
|
||||||
break;
|
break;
|
||||||
|
@ -145,7 +148,7 @@ class ParserIrcNetwork {
|
||||||
|
|
||||||
if (key in CONST.IRC_FIELDS) {
|
if (key in CONST.IRC_FIELDS) {
|
||||||
switch (CONST.IRC_FIELDS[key]) {
|
switch (CONST.IRC_FIELDS[key]) {
|
||||||
case "number":
|
case "number": {
|
||||||
const parsedNum = parseFloat(value);
|
const parsedNum = parseFloat(value);
|
||||||
this.values[key] = parsedNum;
|
this.values[key] = parsedNum;
|
||||||
|
|
||||||
|
@ -154,14 +157,16 @@ class ParserIrcNetwork {
|
||||||
`[IrcNetwork=${this.serverName}] Failed to convert field '${key}' to a number. Got value: '${value}'`
|
`[IrcNetwork=${this.serverName}] Failed to convert field '${key}' to a number. Got value: '${value}'`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "boolean":
|
}
|
||||||
|
case "boolean": {
|
||||||
this.values[key] = value.toLowerCase() === "true";
|
this.values[key] = value.toLowerCase() === "true";
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.values[key] = value;
|
this.values[key] = value;
|
||||||
}*/
|
}*/
|
||||||
|
@ -259,18 +264,22 @@ export class AutodlIrssiConfigParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (match[1]) {
|
switch (match[1]) {
|
||||||
case FILTER:
|
case FILTER: {
|
||||||
this.releaseFilter = new ParserFilter(rightLeftover);
|
this.releaseFilter = new ParserFilter(rightLeftover);
|
||||||
break;
|
break;
|
||||||
case SERVER:
|
}
|
||||||
|
case SERVER: {
|
||||||
this.ircNetwork = new ParserIrcNetwork(rightLeftover);
|
this.ircNetwork = new ParserIrcNetwork(rightLeftover);
|
||||||
break;
|
break;
|
||||||
case CHANNEL:
|
}
|
||||||
|
case CHANNEL: {
|
||||||
this.ircChannel = new ParserIrcChannel(rightLeftover);
|
this.ircChannel = new ParserIrcChannel(rightLeftover);
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ const TypeForm = (props: ClientActionProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setPrevActionType(action.type);
|
setPrevActionType(action.type);
|
||||||
}, [action.type, idx, setFieldValue]);
|
}, [action.type, idx, prevActionType, setFieldValue]);
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
// torrent clients
|
// torrent clients
|
||||||
|
|
|
@ -253,7 +253,7 @@ interface TypeFormProps {
|
||||||
|
|
||||||
const TypeForm = ({ external, idx }: TypeFormProps) => {
|
const TypeForm = ({ external, idx }: TypeFormProps) => {
|
||||||
switch (external.type) {
|
switch (external.type) {
|
||||||
case "EXEC":
|
case "EXEC": {
|
||||||
return (
|
return (
|
||||||
<FilterSection.Section
|
<FilterSection.Section
|
||||||
title="Execute"
|
title="Execute"
|
||||||
|
@ -291,7 +291,8 @@ const TypeForm = ({ external, idx }: TypeFormProps) => {
|
||||||
</FilterSection.Layout>
|
</FilterSection.Layout>
|
||||||
</FilterSection.Section>
|
</FilterSection.Section>
|
||||||
);
|
);
|
||||||
case "WEBHOOK":
|
}
|
||||||
|
case "WEBHOOK": {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterSection.Section
|
<FilterSection.Section
|
||||||
|
@ -363,8 +364,10 @@ const TypeForm = ({ external, idx }: TypeFormProps) => {
|
||||||
</FilterSection.Section>
|
</FilterSection.Section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,8 +97,6 @@ interface CollapsibleSectionProps {
|
||||||
childClassName?: string;
|
childClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(stacksmash76): added text-shadow only for the dark theme - light theme is fine contrast-wise when it comes to headings
|
|
||||||
// ideally, this would need a redesign
|
|
||||||
export const CollapsibleSection = ({
|
export const CollapsibleSection = ({
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
|
@ -137,7 +135,7 @@ export const CollapsibleSection = ({
|
||||||
"flex"
|
"flex"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h3 className="text-xl leading-6 font-bold break-all dark:text-shadow dark:shadow-gray-900 text-gray-900 dark:text-gray-200">
|
<h3 className="text-xl leading-6 font-bold break-all dark:shadow-gray-900 text-gray-900 dark:text-gray-200">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 truncate whitespace-normal break-words">
|
<p className="text-sm text-gray-500 dark:text-gray-400 truncate whitespace-normal break-words">
|
||||||
|
|
|
@ -65,17 +65,22 @@ type Actions =
|
||||||
|
|
||||||
const TableReducer = (state: TableState, action: Actions): TableState => {
|
const TableReducer = (state: TableState, action: Actions): TableState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType.PAGE_CHANGED:
|
case ActionType.PAGE_CHANGED: {
|
||||||
return { ...state, queryPageIndex: action.payload };
|
return { ...state, queryPageIndex: action.payload };
|
||||||
case ActionType.PAGE_SIZE_CHANGED:
|
}
|
||||||
|
case ActionType.PAGE_SIZE_CHANGED: {
|
||||||
return { ...state, queryPageSize: action.payload };
|
return { ...state, queryPageSize: action.payload };
|
||||||
case ActionType.FILTER_CHANGED:
|
}
|
||||||
|
case ActionType.FILTER_CHANGED: {
|
||||||
return { ...state, queryFilters: action.payload };
|
return { ...state, queryFilters: action.payload };
|
||||||
case ActionType.TOTAL_COUNT_CHANGED:
|
}
|
||||||
|
case ActionType.TOTAL_COUNT_CHANGED: {
|
||||||
return { ...state, totalCount: action.payload };
|
return { ...state, totalCount: action.payload };
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
throw new Error(`Unhandled action type: ${action}`);
|
throw new Error(`Unhandled action type: ${action}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReleaseTable = () => {
|
export const ReleaseTable = () => {
|
||||||
|
|
|
@ -42,6 +42,10 @@ interface SortConfig {
|
||||||
direction: "ascending" | "descending";
|
direction: "ascending" | "descending";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isErrorWithMessage = (error: unknown): error is { message: string } => {
|
||||||
|
return typeof error === 'object' && error !== null && 'message' in error;
|
||||||
|
};
|
||||||
|
|
||||||
function useSort(items: ListItemProps["feed"][], config?: SortConfig) {
|
function useSort(items: ListItemProps["feed"][], config?: SortConfig) {
|
||||||
const [sortConfig, setSortConfig] = useState(config);
|
const [sortConfig, setSortConfig] = useState(config);
|
||||||
|
|
||||||
|
@ -257,8 +261,13 @@ const FeedItemDropdown = ({
|
||||||
toast.custom((t) => <Toast type="success" body={`Feed ${feed?.name} was force run successfully.`} t={t} />);
|
toast.custom((t) => <Toast type="success" body={`Feed ${feed?.name} was force run successfully.`} t={t} />);
|
||||||
toggleForceRunModal();
|
toggleForceRunModal();
|
||||||
},
|
},
|
||||||
onError: (error: any) => {
|
onError: (error: unknown) => {
|
||||||
toast.custom((t) => <Toast type="error" body={`Failed to force run ${feed?.name}. Error: ${error.message}`} t={t} />, {
|
let errorMessage = 'An unknown error occurred';
|
||||||
|
if (isErrorWithMessage(error)) {
|
||||||
|
errorMessage = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.custom((t) => <Toast type="error" body={`Failed to force run ${feed?.name}. Error: ${errorMessage}`} t={t} />, {
|
||||||
duration: 10000
|
duration: 10000
|
||||||
});
|
});
|
||||||
toggleForceRunModal();
|
toggleForceRunModal();
|
||||||
|
|
|
@ -621,7 +621,7 @@ export const Events = ({ network, channel }: EventsProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return () => es.close();
|
return () => es.close();
|
||||||
}, [settings]);
|
}, [channel, network.id, settings]);
|
||||||
|
|
||||||
const [isFullscreen, toggleFullscreen] = useToggle(false);
|
const [isFullscreen, toggleFullscreen] = useToggle(false);
|
||||||
|
|
||||||
|
@ -649,7 +649,7 @@ export const Events = ({ network, channel }: EventsProps) => {
|
||||||
};
|
};
|
||||||
if (settings.scrollOnNewLog)
|
if (settings.scrollOnNewLog)
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [logs]);
|
}, [logs, settings.scrollOnNewLog]);
|
||||||
|
|
||||||
// Add a useEffect to clear logs div when settings.scrollOnNewLog changes to prevent duplicate entries.
|
// Add a useEffect to clear logs div when settings.scrollOnNewLog changes to prevent duplicate entries.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { lerpColors } from "tailwind-lerp-colors";
|
import { lerpColors } from "tailwind-lerp-colors";
|
||||||
import plugin from "tailwindcss/plugin";
|
import forms from "@tailwindcss/forms";
|
||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
const extendedColors = lerpColors();
|
const extendedColors = lerpColors();
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
"./src/**/*.{tsx,ts,html,css}",
|
"./src/**/*.{tsx,ts,html,css}"
|
||||||
],
|
],
|
||||||
safelist: [
|
safelist: [
|
||||||
"col-span-1",
|
"col-span-1",
|
||||||
|
@ -19,7 +20,7 @@ module.exports = {
|
||||||
"col-span-9",
|
"col-span-9",
|
||||||
"col-span-10",
|
"col-span-10",
|
||||||
"col-span-11",
|
"col-span-11",
|
||||||
"col-span-12",
|
"col-span-12"
|
||||||
],
|
],
|
||||||
// purge: false,
|
// purge: false,
|
||||||
darkMode: "class", // or 'media' or 'class'
|
darkMode: "class", // or 'media' or 'class'
|
||||||
|
@ -33,7 +34,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
margin: { // for the checkmarks used for regex validation in Filters/Advanced
|
margin: { // for the checkmarks used for regex validation in Filters/Advanced
|
||||||
"2.5": "0.625rem", // 10px, between mb-2 (8px) and mb-3 (12px)
|
"2.5": "0.625rem" // 10px, between mb-2 (8px) and mb-3 (12px)
|
||||||
},
|
},
|
||||||
textShadow: {
|
textShadow: {
|
||||||
DEFAULT: "0 2px 4px var(--tw-shadow-color)"
|
DEFAULT: "0 2px 4px var(--tw-shadow-color)"
|
||||||
|
@ -41,21 +42,12 @@ module.exports = {
|
||||||
boxShadow: {
|
boxShadow: {
|
||||||
table: "rgba(0, 0, 0, 0.1) 0px 4px 16px 0px"
|
table: "rgba(0, 0, 0, 0.1) 0px 4px 16px 0px"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {},
|
extend: {}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
require("@tailwindcss/forms"),
|
forms,
|
||||||
plugin(function ({ matchUtilities, theme }) {
|
]
|
||||||
// Pipe --tw-shadow-color (i.e. shadow-cyan-500/50) to our new text-shadow
|
} satisfies Config;
|
||||||
// Credits: https://www.hyperui.dev/blog/text-shadow-with-tailwindcss
|
|
||||||
// Use it like: text-shadow shadow-cyan-500/50
|
|
||||||
matchUtilities(
|
|
||||||
{ "text-shadow": (value) => ({ textShadow: value }) },
|
|
||||||
{ values: theme("textShadow") }
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -1,33 +1,29 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"lib": [
|
"useDefineForClassFields": true,
|
||||||
"DOM",
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
"DOM.Iterable",
|
|
||||||
"ESNext"
|
|
||||||
],
|
|
||||||
"types": ["vite/client"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": false,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@*": ["./src/*"],
|
"@*": ["./src/*"],
|
||||||
"@app*": ["./src/*"]
|
"@app*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src"],
|
||||||
"./src",
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
"./types",
|
|
||||||
"vite.config.ts"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
10
web/tsconfig.node.json
Normal file
10
web/tsconfig.node.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
|
@ -111,7 +111,15 @@ export default ({ mode }: ConfigEnv) => {
|
||||||
}
|
}
|
||||||
return "assets/[name]-[hash][extname]";
|
return "assets/[name]-[hash][extname]";
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// This ignores the sourcemap warnings after vite 5.x.x introduced by rollup - an upstream dep of vite
|
||||||
|
// ref https://github.com/vitejs/vite/issues/15012#issuecomment-1815854072
|
||||||
|
onLog(level, log, handler) {
|
||||||
|
if (log.cause && (log.cause as { message?: string }).message === `Can't resolve original location of error.`) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
handler(level, log);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue