From e842a7bd42ef3ab963e70d796ad85105fa3ba2ef Mon Sep 17 00:00:00 2001 From: stacksmash76 <98354295+stacksmash76@users.noreply.github.com> Date: Sat, 18 Nov 2023 13:46:16 +0000 Subject: [PATCH] enhancement(web): ui overhaul (#1155) * Various WebUI changes and fixes. * feat(tooltip): make tooltip display upwards * fix(tooltip): place tooltip to the right * fix(web): add missing ml-px to SwitchGroup header current: https://i.imgur.com/2WXstPV.png new: https://i.imgur.com/QGQ49mP.png * fix(web): collapse sections * fix(web): improve freeleech section * fix(web): rename action to action_components Renamed the 'action' folder to 'action_components' to resolve import issues due to case sensitivity. * fix(web): align CollapsibleSection Old Advanced tab: https://i.imgur.com/MXaJ5eJ.png New Advanced tab: https://i.imgur.com/4nPJJRw.png Music tab for comparison: https://i.imgur.com/I59X7ot.png * fix(web): remove invalid CSS class * revert: vertical padding on switchgroup added py-0 on the freeleech part instead * feat(settings): add back log files * fix(settings): irc channels and font sizes * fix(components): radio select roundness * fix(styling): various minor changes * fix(filters): remove jitter fields --------- Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com> Co-authored-by: soup Co-authored-by: ze0s --- internal/database/filter.go | 9 +- internal/http/filter.go | 2 +- web/.eslintrc.js | 4 +- web/package.json | 1 + web/pnpm-lock.yaml | 16 + web/src/api/APIClient.ts | 38 +- web/src/components/Checkbox.tsx | 92 +- web/src/components/ExternalLink.tsx | 2 +- web/src/components/SectionLoader.tsx | 2 +- web/src/components/alerts/ErrorPage.tsx | 8 +- web/src/components/alerts/Warning.tsx | 35 + web/src/components/alerts/index.tsx | 33 +- web/src/components/data-table/Buttons.tsx | 7 +- web/src/components/data-table/Cells.tsx | 10 +- web/src/components/emptystates/index.tsx | 8 +- web/src/components/fields/text.tsx | 6 +- web/src/components/header/Header.tsx | 4 +- web/src/components/header/RightNav.tsx | 23 +- web/src/components/headings/index.tsx | 17 +- web/src/components/inputs/common.tsx | 118 +- web/src/components/inputs/index.ts | 12 +- web/src/components/inputs/input.tsx | 180 +-- web/src/components/inputs/input_wide.tsx | 241 +--- web/src/components/inputs/radio.tsx | 2 +- web/src/components/inputs/select.tsx | 226 ++- web/src/components/inputs/select_wide.tsx | 85 +- web/src/components/inputs/switch.tsx | 117 +- web/src/components/inputs/text.tsx | 16 +- web/src/components/notifications/Toast.tsx | 11 +- web/src/components/panels/index.tsx | 2 +- web/src/components/tooltips/Tooltip.tsx | 5 +- web/src/domain/constants.ts | 40 +- web/src/forms/filters/FilterAddForm.tsx | 7 +- web/src/forms/settings/APIKeyAddForm.tsx | 4 +- .../forms/settings/DownloadClientForms.tsx | 20 +- web/src/forms/settings/IndexerForms.tsx | 220 ++- web/src/forms/settings/IrcForms.tsx | 125 +- web/src/forms/settings/NotificationForms.tsx | 77 +- web/src/index.css | 53 +- web/src/screens/Dashboard.tsx | 2 +- web/src/screens/Logs.tsx | 51 +- web/src/screens/Releases.tsx | 12 +- web/src/screens/Settings.tsx | 15 +- web/src/screens/auth/Login.tsx | 95 +- web/src/screens/auth/Onboarding.tsx | 21 +- web/src/screens/dashboard/ActivityTable.tsx | 10 +- web/src/screens/dashboard/Stats.tsx | 2 +- web/src/screens/filters/Action.tsx | 807 ----------- web/src/screens/filters/Details.tsx | 1237 +++-------------- web/src/screens/filters/External.tsx | 341 ----- web/src/screens/filters/Importer.tsx | 14 +- web/src/screens/filters/List.tsx | 144 +- web/src/screens/filters/_const.ts | 2 +- web/src/screens/filters/sections/Actions.tsx | 315 +++++ web/src/screens/filters/sections/Advanced.tsx | 458 ++++++ web/src/screens/filters/sections/External.tsx | 370 +++++ web/src/screens/filters/sections/General.tsx | 123 ++ .../screens/filters/sections/MoviesAndTV.tsx | 199 +++ web/src/screens/filters/sections/Music.tsx | 187 +++ .../screens/filters/sections/_components.tsx | 157 +++ .../action_components/ActionDeluge.tsx | 66 + .../action_components/ActionPorla.tsx | 63 + .../action_components/ActionQBittorrent.tsx | 177 +++ .../action_components/ActionRTorrent.tsx | 57 + .../action_components/ActionTransmission.tsx | 116 ++ .../action_components/OtherActions.tsx | 134 ++ .../sections/action_components/index.ts | 6 + .../sections/action_components/shared.d.ts | 5 + web/src/screens/filters/sections/index.ts | 6 + web/src/screens/releases/Filters.tsx | 4 +- web/src/screens/releases/ReleaseTable.tsx | 46 +- web/src/screens/settings/Action.tsx | 78 -- web/src/screens/settings/Api.tsx | 89 +- web/src/screens/settings/Application.tsx | 213 +-- web/src/screens/settings/DownloadClient.tsx | 150 +- web/src/screens/settings/Feed.tsx | 151 +- web/src/screens/settings/Indexer.tsx | 156 +-- web/src/screens/settings/Irc.tsx | 264 ++-- web/src/screens/settings/Logs.tsx | 239 ++-- web/src/screens/settings/Notifications.tsx | 107 +- web/src/screens/settings/RegexPlayground.tsx | 16 +- web/src/screens/settings/Releases.tsx | 46 +- web/src/screens/settings/_components.tsx | 84 ++ web/tailwind.config.js | 26 +- 84 files changed, 4378 insertions(+), 4361 deletions(-) create mode 100644 web/src/components/alerts/Warning.tsx delete mode 100644 web/src/screens/filters/Action.tsx delete mode 100644 web/src/screens/filters/External.tsx create mode 100644 web/src/screens/filters/sections/Actions.tsx create mode 100644 web/src/screens/filters/sections/Advanced.tsx create mode 100644 web/src/screens/filters/sections/External.tsx create mode 100644 web/src/screens/filters/sections/General.tsx create mode 100644 web/src/screens/filters/sections/MoviesAndTV.tsx create mode 100644 web/src/screens/filters/sections/Music.tsx create mode 100644 web/src/screens/filters/sections/_components.tsx create mode 100644 web/src/screens/filters/sections/action_components/ActionDeluge.tsx create mode 100644 web/src/screens/filters/sections/action_components/ActionPorla.tsx create mode 100644 web/src/screens/filters/sections/action_components/ActionQBittorrent.tsx create mode 100644 web/src/screens/filters/sections/action_components/ActionRTorrent.tsx create mode 100644 web/src/screens/filters/sections/action_components/ActionTransmission.tsx create mode 100644 web/src/screens/filters/sections/action_components/OtherActions.tsx create mode 100644 web/src/screens/filters/sections/action_components/index.ts create mode 100644 web/src/screens/filters/sections/action_components/shared.d.ts create mode 100644 web/src/screens/filters/sections/index.ts delete mode 100644 web/src/screens/settings/Action.tsx create mode 100644 web/src/screens/settings/_components.tsx diff --git a/internal/database/filter.go b/internal/database/filter.go index 35d927c..a6a26ce 100644 --- a/internal/database/filter.go +++ b/internal/database/filter.go @@ -280,7 +280,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter // filter external var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString - var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32 + var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extExecStatus sql.NullInt32 var extEnabled sql.NullBool if err := rows.Scan( @@ -360,7 +360,6 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter &extWebhookRetryStatus, &extWebhookRetryAttempts, &extWebhookDelaySeconds, - &extWebhookRetryJitterSeconds, ); err != nil { return nil, errors.Wrap(err, "error scanning row") } @@ -551,7 +550,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string // filter external var extName, extType, extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString - var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus, extFilterId sql.NullInt32 + var extId, extIndex, extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extExecStatus, extFilterId sql.NullInt32 var extEnabled sql.NullBool if err := rows.Scan( @@ -631,7 +630,6 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string &extWebhookRetryStatus, &extWebhookRetryAttempts, &extWebhookDelaySeconds, - &extWebhookRetryJitterSeconds, &extFilterId, ); err != nil { return nil, errors.Wrap(err, "error scanning row") @@ -756,7 +754,7 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int) // filter external var extExecCmd, extExecArgs, extWebhookHost, extWebhookMethod, extWebhookHeaders, extWebhookData, extWebhookRetryStatus sql.NullString - var extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extWebhookRetryJitterSeconds, extExecStatus sql.NullInt32 + var extWebhookStatus, extWebhookRetryAttempts, extWebhookDelaySeconds, extExecStatus sql.NullInt32 if err := rows.Scan( &external.ID, @@ -775,7 +773,6 @@ func (r *FilterRepo) FindExternalFiltersByID(ctx context.Context, filterId int) &extWebhookRetryStatus, &extWebhookRetryAttempts, &extWebhookDelaySeconds, - &extWebhookRetryJitterSeconds, ); err != nil { return nil, errors.Wrap(err, "error scanning row") } diff --git a/internal/http/filter.go b/internal/http/filter.go index 0d4b7cb..6ce9fb1 100644 --- a/internal/http/filter.go +++ b/internal/http/filter.go @@ -123,7 +123,7 @@ func (h filterHandler) getByID(w http.ResponseWriter, r *http.Request) { return } - h.encoder.StatusInternalError(w) + h.encoder.Error(w, err) return } diff --git a/web/.eslintrc.js b/web/.eslintrc.js index c3f2007..9136efe 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -19,7 +19,7 @@ module.exports = { // 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": 1 }], + indent: ["warn", 2, { "SwitchCase": 0 }], // Don't enforce any particular brace style curly: "off", // Allow only vars starting with _ to be ununsed vars @@ -64,7 +64,7 @@ module.exports = { "@typescript-eslint/quotes": ["error", "double"], semi: "off", "@typescript-eslint/semi": ["warn", "always"], - indent: ["warn", 2, { "SwitchCase": 1 }], + indent: ["warn", 2, { "SwitchCase": 0 }], "@typescript-eslint/indent": "off", "@typescript-eslint/comma-dangle": "warn", "keyword-spacing": "off", diff --git a/web/package.json b/web/package.json index 246e9b0..c5926e0 100644 --- a/web/package.json +++ b/web/package.json @@ -70,6 +70,7 @@ "react-table": "^7.8.0", "react-textarea-autosize": "^8.5.3", "stacktracey": "^2.1.8", + "tailwind-lerp-colors": "1.2.1", "tailwindcss": "^3.3.3", "typescript": "^5.2.2", "vite": "^4.5.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index f6f2574..51855a6 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -131,6 +131,9 @@ dependencies: stacktracey: specifier: ^2.1.8 version: 2.1.8 + tailwind-lerp-colors: + specifier: 1.2.1 + version: 1.2.1 tailwindcss: specifier: ^3.3.3 version: 3.3.3 @@ -2794,6 +2797,10 @@ packages: fsevents: 2.3.3 dev: false + /chroma-js@2.4.2: + resolution: {integrity: sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==} + dev: false + /client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false @@ -5277,6 +5284,15 @@ packages: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} dev: false + /tailwind-lerp-colors@1.2.1: + resolution: {integrity: sha512-VC09rQv7bAjw2MEIeXxqgXKRQ5rW9z+1N+JAPvc/ARbQSIdmmBaweh3vR5YpxsXXW12SUylJutufoojFKmJeeQ==} + dependencies: + chroma-js: 2.4.2 + tailwindcss: 3.3.3 + transitivePeerDependencies: + - ts-node + dev: false + /tailwindcss@3.3.3: resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} engines: {node: '>=14.0.0'} diff --git a/web/src/api/APIClient.ts b/web/src/api/APIClient.ts index 2434a9a..8f2e94a 100644 --- a/web/src/api/APIClient.ts +++ b/web/src/api/APIClient.ts @@ -82,27 +82,27 @@ export async function HttpClient( const response = await window.fetch(`${baseUrl()}${endpoint}`, init); switch (response.status) { - case 204: + case 204: // 204 contains no data, but indicates success - return Promise.resolve({} as T); - case 401: + return Promise.resolve({} as T); + case 401: // Remove auth info from localStorage - AuthContext.reset(); + AuthContext.reset(); - // Show an error toast to notify the user what occurred - return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`)); - case 404: - return Promise.reject(new Error(`[404] Not found: "${endpoint}"`)); - case 500: - const health = await window.fetch(`${baseUrl()}api/healthz/liveness`); - if (!health.ok) { - return Promise.reject( - new Error(`[500] Offline (Internal server error): "${endpoint}"`, { cause: "OFFLINE" }) - ); - } - break; - default: - break; + // Show an error toast to notify the user what occurred + return Promise.reject(new Error(`[401] Unauthorized: "${endpoint}"`)); + case 404: + return Promise.reject(new Error(`[404] Not found: "${endpoint}"`)); + case 500: + const health = await window.fetch(`${baseUrl()}api/healthz/liveness`); + if (!health.ok) { + return Promise.reject( + new Error(`[500] Offline (Internal server error): "${endpoint}"`, { cause: "OFFLINE" }) + ); + } + break; + default: + break; } const isJson = response.headers.get("Content-Type")?.includes("application/json"); @@ -249,7 +249,7 @@ export const APIClient = { }), toggleEnable: (id: number, enabled: boolean) => appClient.Patch(`api/indexer/${id}/enabled`, { body: { enabled } - }), + }) }, irc: { getNetworks: () => appClient.Get("api/irc"), diff --git a/web/src/components/Checkbox.tsx b/web/src/components/Checkbox.tsx index e135a89..87cabc1 100644 --- a/web/src/components/Checkbox.tsx +++ b/web/src/components/Checkbox.tsx @@ -4,36 +4,82 @@ */ import { Switch } from "@headlessui/react"; +import { classNames } from "@utils"; interface CheckboxProps { - value: boolean; - setValue: (newValue: boolean) => void; - label: string; - description?: string; + value: boolean; + setValue: (newValue: boolean) => void; + label?: string; + description?: string; + className?: string; + disabled?: boolean; } -export const Checkbox = ({ label, description, value, setValue }: CheckboxProps) => ( - -
- - {label} - - {description === undefined ? null : ( - - {description} - - )} -
+export const Checkbox = ({ + label, + description, + value, + className, + setValue, + disabled +}: CheckboxProps) => ( + { + e.stopPropagation(); + e.nativeEvent.stopImmediatePropagation(); + }} + > + {(label || description) ? ( +
+ {label ? ( + + {label} + + ) : null} + {description ? ( + + {description} + + ) : null} +
+ ) : null} { + !disabled && setValue(newValue); + }} + className={classNames( + disabled + ? "cursor-not-allowed bg-gray-450 dark:bg-gray-700 border-gray-375 dark:border-gray-800" + : ( + value + ? "cursor-pointer bg-blue-600 border-blue-525" + : "cursor-pointer bg-gray-300 dark:bg-gray-700 border-gray-375 dark:border-gray-600" + ), + "border relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors" + )} > + className={classNames( + value ? "translate-x-6" : "translate-x-[0.15rem]", + disabled ? "bg-gray-650 dark:bg-gray-800" : "bg-white", + "inline-flex items-center align-center h-4 w-4 transform rounded-full transition ring-0 shadow" + )} + > + {value + ? ( + + ) + : ( + + )} +
-); \ No newline at end of file +); diff --git a/web/src/components/ExternalLink.tsx b/web/src/components/ExternalLink.tsx index 86691b9..3143487 100644 --- a/web/src/components/ExternalLink.tsx +++ b/web/src/components/ExternalLink.tsx @@ -16,5 +16,5 @@ export const ExternalLink = ({ href, className, children }: ExternalLinkProps) = ); export const DocsLink = ({ href }: { href: string; }) => ( - {href} + {href} ); diff --git a/web/src/components/SectionLoader.tsx b/web/src/components/SectionLoader.tsx index dee4d0a..94cfe5c 100644 --- a/web/src/components/SectionLoader.tsx +++ b/web/src/components/SectionLoader.tsx @@ -29,4 +29,4 @@ export const SectionLoader = ({ $size }: SectionLoaderProps) => { ); } -} +}; diff --git a/web/src/components/alerts/ErrorPage.tsx b/web/src/components/alerts/ErrorPage.tsx index fe375ec..884c94e 100644 --- a/web/src/components/alerts/ErrorPage.tsx +++ b/web/src/components/alerts/ErrorPage.tsx @@ -20,10 +20,10 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => { const parseTitle = () => { switch (error?.cause) { - case "OFFLINE": - return "Connection to Autobrr failed! Check the application state and verify your connectivity."; - default: - return "We caught an unrecoverable error!"; + case "OFFLINE": + return "Connection to Autobrr failed! Check the application state and verify your connectivity."; + default: + return "We caught an unrecoverable error!"; } }; diff --git a/web/src/components/alerts/Warning.tsx b/web/src/components/alerts/Warning.tsx new file mode 100644 index 0000000..52996f5 --- /dev/null +++ b/web/src/components/alerts/Warning.tsx @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 - 2023, Ludvig Lundgren and the autobrr contributors. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import { classNames } from "@utils"; + +interface WarningAlertProps { + text: string | JSX.Element; + alert?: string; + colors?: string; + className?: string; +} + +export const WarningAlert = ({ text, alert, colors, className }: WarningAlertProps) => ( +
+ + Info +
+ {alert ?? "Warning!"} + {" "}{text} +
+
+); diff --git a/web/src/components/alerts/index.tsx b/web/src/components/alerts/index.tsx index b7b7437..e6b1e9f 100644 --- a/web/src/components/alerts/index.tsx +++ b/web/src/components/alerts/index.tsx @@ -3,34 +3,5 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; - -interface props { - title?: string; - text: string; -} - -export function AlertWarning({ title, text }: props) { - return ( -
-
-
-
-
- {title ? ( -

{title}

- ) : null} -
-

{text}

-
-
-
-
- ); -} - -export { ErrorPage } from "./ErrorPage"; \ No newline at end of file +export * from "./ErrorPage"; +export * from "./Warning"; diff --git a/web/src/components/data-table/Buttons.tsx b/web/src/components/data-table/Buttons.tsx index f59dbbc..bb4e089 100644 --- a/web/src/components/data-table/Buttons.tsx +++ b/web/src/components/data-table/Buttons.tsx @@ -32,11 +32,14 @@ export const PageButton = ({ children, className, disabled, onClick }: ButtonPro type="button" className={classNames( className ?? "", - "cursor-pointer inline-flex items-center p-1.5 border border-gray-300 dark:border-gray-700 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600" + disabled + ? "cursor-not-allowed text-gray-500 dark:text-gray-500 border-gray-300 dark:border-gray-700 dark:bg-gray-800" + : "cursor-pointer text-gray-500 dark:text-gray-350 border-gray-300 dark:border-gray-700 dark:bg-gray-850 hover:bg-gray-100 dark:hover:bg-gray-700", + "inline-flex items-center p-1.5 border text-sm font-medium transition" )} disabled={disabled} onClick={onClick} > {children} -); \ No newline at end of file +); diff --git a/web/src/components/data-table/Cells.tsx b/web/src/components/data-table/Cells.tsx index 1a54ad1..e58a6f6 100644 --- a/web/src/components/data-table/Cells.tsx +++ b/web/src/components/data-table/Cells.tsx @@ -8,7 +8,7 @@ import { toast } from "react-hot-toast"; import { formatDistanceToNowStrict } from "date-fns"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { ArrowPathIcon, CheckIcon } from "@heroicons/react/24/solid"; -import { ClockIcon, ExclamationCircleIcon, NoSymbolIcon } from "@heroicons/react/24/outline"; +import { ClockIcon, XMarkIcon, NoSymbolIcon } from "@heroicons/react/24/outline"; import { APIClient } from "@api/APIClient"; import { classNames, simplifyDate } from "@utils"; @@ -95,7 +95,7 @@ const RetryActionButton = ({ status }: RetryActionButtonProps) => { }; return ( - )} @@ -95,4 +104,4 @@ export const RightNav = (props: RightNavProps) => { ); -} +}; diff --git a/web/src/components/headings/index.tsx b/web/src/components/headings/index.tsx index 49ad6ac..c1e3fdd 100644 --- a/web/src/components/headings/index.tsx +++ b/web/src/components/headings/index.tsx @@ -3,16 +3,15 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -import { FC } from "react"; - interface Props { - title: string; - subtitle: string; + title: string; + subtitle: string | React.ReactNode; + className?: string; } -export const TitleSubtitle: FC = ({ title, subtitle }) => ( -
-

{title}

-

{subtitle}

+export const TitleSubtitle = ({ title, subtitle, className }: Props) => ( +
+

{title}

+

{subtitle}

-); \ No newline at end of file +); diff --git a/web/src/components/inputs/common.tsx b/web/src/components/inputs/common.tsx index adb22db..8cff5ad 100644 --- a/web/src/components/inputs/common.tsx +++ b/web/src/components/inputs/common.tsx @@ -4,73 +4,87 @@ */ import { Field, FieldProps } from "formik"; +import { components } from "react-select"; +import type { + InputProps, + ControlProps, + MenuProps, + OptionProps, + IndicatorSeparatorProps, + DropdownIndicatorProps +} from "react-select"; + import { classNames } from "@utils"; -import { DocsTooltip } from "@components/tooltips/DocsTooltip"; interface ErrorFieldProps { - name: string; - classNames?: string; + name: string; + classNames?: string; } -const ErrorField = ({ name, classNames }: ErrorFieldProps) => ( -
- - {({ meta: { touched, error } }: FieldProps) => - touched && error ? {error} : null - } - -
+export const ErrorField = ({ name, classNames }: ErrorFieldProps) => ( + + {({ meta: { touched, error } }: FieldProps) => + touched && error ? {error} : null + } + ); interface RequiredFieldProps { required?: boolean } -const RequiredField = ({ required }: RequiredFieldProps) => ( +export const RequiredField = ({ required }: RequiredFieldProps) => ( <> {required && *} ); -interface CheckboxFieldProps { - name: string; - label: string; - sublabel?: string; - disabled?: boolean; - tooltip?: JSX.Element; -} - -const CheckboxField = ({ - name, - label, - sublabel, - tooltip, - disabled -}: CheckboxFieldProps) => ( -
-
- -
-
- -

{sublabel}

-
-
+export const SelectInput = (props: InputProps) => ( + ); -export { ErrorField, RequiredField, CheckboxField }; +export const SelectControl = (props: ControlProps) => ( + +); + +export const SelectMenu = (props: MenuProps) => ( + +); + +export const SelectOption = (props: OptionProps) => ( + +); + +export const IndicatorSeparator = (props: IndicatorSeparatorProps) => ( + +); + +export const DropdownIndicator = (props: DropdownIndicatorProps) => ( + +); diff --git a/web/src/components/inputs/index.ts b/web/src/components/inputs/index.ts index 2853ff3..29d9611 100644 --- a/web/src/components/inputs/index.ts +++ b/web/src/components/inputs/index.ts @@ -3,10 +3,10 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -export { ErrorField, CheckboxField } from "./common"; -export { TextField, NumberField, PasswordField, RegexField } from "./input"; -export { NumberFieldWide, PasswordFieldWide, SwitchGroupWide, SwitchGroupWideRed, TextFieldWide } from "./input_wide"; -export { RadioFieldsetWide } from "./radio"; -export { MultiSelect, Select, SelectWide, DownloadClientSelect, IndexerMultiSelect } from "./select"; -export { SwitchGroup } from "./switch"; +export * from "./common"; +export * from "./input"; +export * from "./input_wide"; +export * from "./radio"; +export * from "./select"; +export * from "./switch"; diff --git a/web/src/components/inputs/input.tsx b/web/src/components/inputs/input.tsx index d013a2b..bb16128 100644 --- a/web/src/components/inputs/input.tsx +++ b/web/src/components/inputs/input.tsx @@ -18,6 +18,7 @@ interface TextFieldProps { name: string; defaultValue?: string; label?: string; + required?: boolean; placeholder?: string; columns?: COL_WIDTHS; autoComplete?: string; @@ -30,6 +31,7 @@ export const TextField = ({ name, defaultValue, label, + required, placeholder, columns, autoComplete, @@ -39,25 +41,27 @@ export const TextField = ({ }: TextFieldProps) => ( @@ -174,20 +182,19 @@ export const RegexField = ({ return ( @@ -315,20 +322,22 @@ export const RegexTextAreaField = ({ return ( @@ -406,17 +415,16 @@ export const TextArea = ({ }: TextAreaProps) => (