From 5e29564f0329880954ff827e30de24f00c6c977a Mon Sep 17 00:00:00 2001 From: Ludvig Lundgren Date: Thu, 23 Dec 2021 22:01:59 +0100 Subject: [PATCH] Refactor(web): Replace final-form with Formik and cleanup (#46) * refactor: begin to replace final-form * refactor: replace final-form with formik n cleanup --- web/package.json | 6 +- web/src/App.tsx | 2 +- web/src/api/APIClient.ts | 2 +- web/src/components/EmptyListState.tsx | 22 - web/src/components/FilterActionList.tsx | 495 ------------------ web/src/components/alerts/index.ts | 1 - .../alerts/{warning.tsx => index.tsx} | 6 +- web/src/components/debug.tsx | 8 +- web/src/components/empty/EmptySimple.tsx | 27 - web/src/components/emptystates/index.tsx | 48 ++ .../headings/{TitleSubtitle.tsx => index.tsx} | 8 +- web/src/components/inputs/Error.tsx | 20 - .../components/inputs/MultiSelectField.tsx | 50 -- web/src/components/inputs/PasswordField.tsx | 47 -- web/src/components/inputs/RadioFieldset.tsx | 60 --- web/src/components/inputs/SwitchGroup.tsx | 57 -- web/src/components/inputs/TextAreaWide.tsx | 40 -- web/src/components/inputs/TextField.tsx | 48 -- web/src/components/inputs/TextFieldWide.tsx | 49 -- web/src/components/inputs/common.tsx | 17 + .../components/inputs/compact/NumberField.tsx | 47 -- .../components/inputs/compact/SelectField.tsx | 111 ---- web/src/components/inputs/compact/index.ts | 2 - web/src/components/inputs/index.ts | 14 +- web/src/components/inputs/input.tsx | 159 ++++++ web/src/components/inputs/input_wide.tsx | 217 ++++++++ web/src/components/inputs/radio.tsx | 115 ++++ web/src/components/inputs/select.tsx | 271 ++++++++++ .../inputs/switch.tsx} | 82 ++- .../components/inputs/wide/NumberField.tsx | 64 --- .../components/inputs/wide/PasswordField.tsx | 56 -- .../inputs/wide/RadioFieldsetWide.tsx | 105 ---- .../components/inputs/wide/SelectField.tsx | 111 ---- web/src/components/inputs/wide/index.ts | 4 - web/src/components/modals/index.ts | 1 - .../modals/{Delete.tsx => index.tsx} | 10 +- web/src/components/notifications/Toast.tsx | 70 ++- web/src/components/panels/index.ts | 1 - .../panels/{SlideOver.tsx => index.tsx} | 31 +- web/src/domain/interfaces.ts | 1 + web/src/forms/filters/FilterActionAddForm.tsx | 415 --------------- .../forms/filters/FilterActionUpdateForm.tsx | 318 ----------- web/src/forms/filters/FilterAddForm.tsx | 32 +- web/src/forms/index.ts | 2 - .../forms/settings/DownloadClientForms.tsx | 470 +++++++++-------- web/src/forms/settings/IndexerForms.tsx | 162 +++--- web/src/forms/settings/IrcForms.tsx | 212 +++----- web/src/screens/Dashboard.tsx | 2 +- web/src/screens/Settings.tsx | 2 +- web/src/screens/auth/login.tsx | 64 +-- web/src/screens/filters/details.tsx | 115 +--- .../filters/inputs/DownloadClientSelect.tsx | 109 ---- .../screens/filters/inputs/MultiSelect.tsx | 56 -- .../screens/filters/inputs/NumberField.tsx | 52 -- web/src/screens/filters/inputs/Select.tsx | 116 ---- web/src/screens/filters/inputs/Switch.tsx | 60 --- web/src/screens/filters/inputs/TextField.tsx | 51 -- web/src/screens/filters/inputs/index.ts | 7 - web/src/screens/filters/list.tsx | 4 +- web/src/screens/settings/Application.tsx | 96 ++-- web/src/screens/settings/DownloadClient.tsx | 19 +- web/src/screens/settings/Indexer.tsx | 4 +- web/src/screens/settings/Irc.tsx | 4 +- web/src/styles/utils.ts | 7 - web/src/utils/{utils.ts => index.ts} | 7 + web/yarn.lock | 61 +-- 66 files changed, 1523 insertions(+), 3409 deletions(-) delete mode 100644 web/src/components/EmptyListState.tsx delete mode 100644 web/src/components/FilterActionList.tsx delete mode 100644 web/src/components/alerts/index.ts rename web/src/components/alerts/{warning.tsx => index.tsx} (87%) delete mode 100644 web/src/components/empty/EmptySimple.tsx create mode 100644 web/src/components/emptystates/index.tsx rename web/src/components/headings/{TitleSubtitle.tsx => index.tsx} (68%) delete mode 100644 web/src/components/inputs/Error.tsx delete mode 100644 web/src/components/inputs/MultiSelectField.tsx delete mode 100644 web/src/components/inputs/PasswordField.tsx delete mode 100644 web/src/components/inputs/RadioFieldset.tsx delete mode 100644 web/src/components/inputs/SwitchGroup.tsx delete mode 100644 web/src/components/inputs/TextAreaWide.tsx delete mode 100644 web/src/components/inputs/TextField.tsx delete mode 100644 web/src/components/inputs/TextFieldWide.tsx create mode 100644 web/src/components/inputs/common.tsx delete mode 100644 web/src/components/inputs/compact/NumberField.tsx delete mode 100644 web/src/components/inputs/compact/SelectField.tsx delete mode 100644 web/src/components/inputs/compact/index.ts create mode 100644 web/src/components/inputs/input.tsx create mode 100644 web/src/components/inputs/input_wide.tsx create mode 100644 web/src/components/inputs/radio.tsx create mode 100644 web/src/components/inputs/select.tsx rename web/src/{screens/filters/inputs/SwitchGroup.tsx => components/inputs/switch.tsx} (52%) delete mode 100644 web/src/components/inputs/wide/NumberField.tsx delete mode 100644 web/src/components/inputs/wide/PasswordField.tsx delete mode 100644 web/src/components/inputs/wide/RadioFieldsetWide.tsx delete mode 100644 web/src/components/inputs/wide/SelectField.tsx delete mode 100644 web/src/components/inputs/wide/index.ts delete mode 100644 web/src/components/modals/index.ts rename web/src/components/modals/{Delete.tsx => index.tsx} (96%) delete mode 100644 web/src/components/panels/index.ts rename web/src/components/panels/{SlideOver.tsx => index.tsx} (91%) delete mode 100644 web/src/forms/filters/FilterActionAddForm.tsx delete mode 100644 web/src/forms/filters/FilterActionUpdateForm.tsx delete mode 100644 web/src/screens/filters/inputs/DownloadClientSelect.tsx delete mode 100644 web/src/screens/filters/inputs/MultiSelect.tsx delete mode 100644 web/src/screens/filters/inputs/NumberField.tsx delete mode 100644 web/src/screens/filters/inputs/Select.tsx delete mode 100644 web/src/screens/filters/inputs/Switch.tsx delete mode 100644 web/src/screens/filters/inputs/TextField.tsx delete mode 100644 web/src/screens/filters/inputs/index.ts delete mode 100644 web/src/styles/utils.ts rename web/src/utils/{utils.ts => index.ts} (86%) diff --git a/web/package.json b/web/package.json index a038462..259b63a 100644 --- a/web/package.json +++ b/web/package.json @@ -2,7 +2,7 @@ "name": "web", "version": "0.1.0", "private": true, - "proxy": "http://localhost:8989", + "proxy": "http://127.0.0.1:8989", "homepage": ".", "dependencies": { "@craco/craco": "^6.1.2", @@ -16,14 +16,10 @@ "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "date-fns": "^2.25.0", - "final-form": "^4.20.2", - "final-form-arrays": "^3.0.2", "formik": "^2.2.9", "react": "^17.0.2", "react-cookie": "^4.1.1", "react-dom": "^17.0.2", - "react-final-form": "^6.5.3", - "react-final-form-arrays": "^3.1.3", "react-hot-toast": "^2.1.1", "react-multi-select-component": "^4.0.2", "react-query": "^3.18.1", diff --git a/web/src/App.tsx b/web/src/App.tsx index 7c60bb6..68956ab 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -5,7 +5,7 @@ import Logout from "./screens/auth/logout"; import Base from "./screens/Base"; import { ReactQueryDevtools } from "react-query/devtools"; import Layout from "./components/Layout"; -import { baseUrl } from "./utils/utils"; +import { baseUrl } from "./utils"; function Protected() { return ( diff --git a/web/src/api/APIClient.ts b/web/src/api/APIClient.ts index b163069..52f6f36 100644 --- a/web/src/api/APIClient.ts +++ b/web/src/api/APIClient.ts @@ -1,5 +1,5 @@ import {Action, DownloadClient, Filter, Indexer, Network} from "../domain/interfaces"; -import {baseUrl, sseBaseUrl} from "../utils/utils"; +import {baseUrl, sseBaseUrl} from "../utils"; function baseClient(endpoint: string, method: string, { body, ...customConfig}: any = {}) { let baseURL = baseUrl() diff --git a/web/src/components/EmptyListState.tsx b/web/src/components/EmptyListState.tsx deleted file mode 100644 index 65d9b8b..0000000 --- a/web/src/components/EmptyListState.tsx +++ /dev/null @@ -1,22 +0,0 @@ -interface props { - text: string; - buttonText?: string; - buttonOnClick?: any; -} - -export function EmptyListState({ text, buttonText, buttonOnClick }: props) { - return ( -
-

{text}

- {buttonText && buttonOnClick && ( - - )} -
- ) -} \ No newline at end of file diff --git a/web/src/components/FilterActionList.tsx b/web/src/components/FilterActionList.tsx deleted file mode 100644 index 3129232..0000000 --- a/web/src/components/FilterActionList.tsx +++ /dev/null @@ -1,495 +0,0 @@ -import { Action, DownloadClient } from "../domain/interfaces"; -import { Fragment, useEffect, useRef } from "react"; -import { Dialog, Listbox, Switch, Transition } from "@headlessui/react"; -import { classNames } from "../styles/utils"; -import { - CheckIcon, - ChevronRightIcon, - SelectorIcon, -} from "@heroicons/react/solid"; -import { useToggle } from "../hooks/hooks"; -import { useMutation } from "react-query"; -import { Field, Form } from "react-final-form"; -import { SwitchGroup, TextField } from "./inputs"; -import { NumberField, SelectField } from "./inputs/compact"; -import DEBUG from "./debug"; -import APIClient from "../api/APIClient"; -import { queryClient } from "../App"; -import { ActionTypeNameMap, ActionTypeOptions } from "../domain/constants"; -import { AlertWarning } from "./alerts"; -import { DeleteModal } from "./modals"; - -interface DownloadClientSelectProps { - name: string; - action: Action; - clients: DownloadClient[]; -} - -function DownloadClientSelect({ - name, - action, - clients, -}: DownloadClientSelectProps) { - return ( -
- ( - - {({ open }) => ( - <> - - Client - -
- - - {input.value - ? clients.find((c) => c.id === input.value)!.name - : "Choose a client"} - - {/*Choose a client*/} - - - - - - - {clients - .filter((c) => c.type === action.type) - .map((client: any) => ( - - classNames( - active - ? "text-white bg-indigo-600" - : "text-gray-900", - "cursor-default select-none relative py-2 pl-3 pr-9" - ) - } - value={client.id} - > - {({ selected, active }) => ( - <> - - {client.name} - - - {selected ? ( - - - ) : null} - - )} - - ))} - - -
- - )} -
- )} - /> -
- ); -} - -interface FilterListProps { - actions: Action[]; - clients: DownloadClient[]; - filterID: number; -} - -export function FilterActionList({ - actions, - clients, - filterID, -}: FilterListProps) { - useEffect(() => { - // console.log("render list") - }, []); - - return ( -
- -
- ); -} - -interface ListItemProps { - action: Action; - clients: DownloadClient[]; - filterID: number; - idx: number; -} - -function ListItem({ action, clients, filterID, idx }: ListItemProps) { - const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false); - const [edit, toggleEdit] = useToggle(false); - - const deleteMutation = useMutation( - (actionID: number) => APIClient.actions.delete(actionID), - { - onSuccess: () => { - queryClient.invalidateQueries(["filter", filterID]); - toggleDeleteModal(); - }, - } - ); - - const enabledMutation = useMutation( - (actionID: number) => APIClient.actions.toggleEnable(actionID), - { - onSuccess: () => { - queryClient.invalidateQueries(["filter", filterID]); - }, - } - ); - - const updateMutation = useMutation( - (action: Action) => APIClient.actions.update(action), - { - onSuccess: () => { - queryClient.invalidateQueries(["filter", filterID]); - }, - } - ); - - const toggleActive = () => { - enabledMutation.mutate(action.id); - }; - - useEffect(() => { }, [action]); - - const cancelButtonRef = useRef(null); - - const deleteAction = () => { - deleteMutation.mutate(action.id); - }; - - const onSubmit = (action: Action) => { - // TODO clear data depending on type - updateMutation.mutate(action); - }; - - const TypeForm = (action: Action) => { - switch (action.type) { - case "TEST": - return ( - - ); - case "EXEC": - return ( -
-
- - -
-
- ); - case "WATCH_FOLDER": - return ( -
- -
- ); - case "QBITTORRENT": - return ( -
-
- - -
- -
-
- -
- - -
- -
- - -
- -
-
- -
-
-
- ); - case "DELUGE_V1": - case "DELUGE_V2": - return ( -
-
- - -
- -
-
- -
- -
- -
- - -
- -
-
- -
-
-
- ); - case "RADARR": - case "SONARR": - case "LIDARR": - return ( -
- -
- ); - - default: - return null; - } - }; - - return ( -
  • -
    - - Use setting - - -
    - - {edit && ( -
    - - - - - - -
    - {({ handleSubmit, values }) => { - return ( - -
    - - - -
    - - {TypeForm(values)} - -
    -
    - - -
    - - -
    -
    -
    - - - - ); - }} - -
    - )} -
  • - ); -} diff --git a/web/src/components/alerts/index.ts b/web/src/components/alerts/index.ts deleted file mode 100644 index 8f5135b..0000000 --- a/web/src/components/alerts/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AlertWarning } from "./warning"; diff --git a/web/src/components/alerts/warning.tsx b/web/src/components/alerts/index.tsx similarity index 87% rename from web/src/components/alerts/warning.tsx rename to web/src/components/alerts/index.tsx index 0d9908f..8833122 100644 --- a/web/src/components/alerts/warning.tsx +++ b/web/src/components/alerts/index.tsx @@ -1,12 +1,12 @@ + import { ExclamationIcon } from "@heroicons/react/solid"; -import React from "react"; interface props { title: string; text: string; } -function AlertWarning({ title, text }: props) { +export function AlertWarning({ title, text }: props) { return (
    @@ -28,5 +28,3 @@ function AlertWarning({ title, text }: props) {
    ); } - -export default AlertWarning; diff --git a/web/src/components/debug.tsx b/web/src/components/debug.tsx index 9a59580..7422fef 100644 --- a/web/src/components/debug.tsx +++ b/web/src/components/debug.tsx @@ -1,4 +1,10 @@ -const DEBUG = ({ values }: any) => { +import { FC } from "react" + +interface DebugProps { + values: unknown; +} + +const DEBUG: FC = ({ values }) => { if (process.env.NODE_ENV !== "development") { return null; } diff --git a/web/src/components/empty/EmptySimple.tsx b/web/src/components/empty/EmptySimple.tsx deleted file mode 100644 index 75679c7..0000000 --- a/web/src/components/empty/EmptySimple.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { PlusIcon } from "@heroicons/react/solid"; - -interface props { - title: string; - subtitle: string; - buttonText: string; - buttonAction: any; -} - -const EmptySimple = ({ title, subtitle, buttonText, buttonAction }: props) => ( -
    -

    {title}

    -

    {subtitle}

    -
    - -
    -
    -) - -export default EmptySimple; \ No newline at end of file diff --git a/web/src/components/emptystates/index.tsx b/web/src/components/emptystates/index.tsx new file mode 100644 index 0000000..7796afd --- /dev/null +++ b/web/src/components/emptystates/index.tsx @@ -0,0 +1,48 @@ +import { PlusIcon } from "@heroicons/react/solid"; + +interface EmptySimpleProps { + title: string; + subtitle: string; + buttonText: string; + buttonAction: any; +} + +export const EmptySimple = ({ title, subtitle, buttonText, buttonAction }: EmptySimpleProps) => ( +
    +

    {title}

    +

    {subtitle}

    +
    + +
    +
    +) + +interface EmptyListStateProps { + text: string; + buttonText?: string; + buttonOnClick?: any; +} + +export function EmptyListState({ text, buttonText, buttonOnClick }: EmptyListStateProps) { + return ( +
    +

    {text}

    + {buttonText && buttonOnClick && ( + + )} +
    + ) +} \ No newline at end of file diff --git a/web/src/components/headings/TitleSubtitle.tsx b/web/src/components/headings/index.tsx similarity index 68% rename from web/src/components/headings/TitleSubtitle.tsx rename to web/src/components/headings/index.tsx index 1da65af..8f2cede 100644 --- a/web/src/components/headings/TitleSubtitle.tsx +++ b/web/src/components/headings/index.tsx @@ -1,15 +1,13 @@ -import React from "react"; +import { FC } from "react"; interface Props { title: string; subtitle: string; } -const TitleSubtitle: React.FC = ({ title, subtitle }) => ( +export const TitleSubtitle: FC = ({ title, subtitle }) => (

    {title}

    {subtitle}

    -) - -export default TitleSubtitle; \ No newline at end of file +) \ No newline at end of file diff --git a/web/src/components/inputs/Error.tsx b/web/src/components/inputs/Error.tsx deleted file mode 100644 index 73f4693..0000000 --- a/web/src/components/inputs/Error.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import { Field } from "react-final-form"; - -interface Props { - name: string; - classNames?: string; - subscribe?: any; -} - -const Error: React.FC = ({ name, classNames }) => ( - - touched && error ? {error} : null - } - /> -); - -export default Error; diff --git a/web/src/components/inputs/MultiSelectField.tsx b/web/src/components/inputs/MultiSelectField.tsx deleted file mode 100644 index 4ec41c4..0000000 --- a/web/src/components/inputs/MultiSelectField.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import {Field} from "react-final-form"; -import { MultiSelect } from "react-multi-select-component"; -import {classNames, COL_WIDTHS} from "../../styles/utils"; - -interface Props { - label?: string; - options?: [] | any; - name: string; - className?: string; - columns?: COL_WIDTHS; -} - -const MultiSelectField: React.FC = ({ - name, - label, - options, - className, - columns - }) => ( -
    - - val && val.map((item: any) => item.value)} - format={val => - val && - val.map((item: any) => options.find((o: any) => o.value === item)) - } - render={({input, meta}) => ( - - )} - /> -
    - ); - -export default MultiSelectField; diff --git a/web/src/components/inputs/PasswordField.tsx b/web/src/components/inputs/PasswordField.tsx deleted file mode 100644 index ffd904f..0000000 --- a/web/src/components/inputs/PasswordField.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Field } from "react-final-form"; -import React from "react"; -import Error from "./Error"; -import { classNames } from "../../styles/utils"; - -type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; - -interface Props { - name: string; - label?: string; - placeholder?: string; - columns?: COL_WIDTHS; - className?: string; - autoComplete?: string; -} - -const PasswordField: React.FC = ({ name, label, placeholder, columns, className, autoComplete }) => ( -
    - {label && ( - - )} - ( - - )} - /> -
    - -
    -
    -) - -export default PasswordField; \ No newline at end of file diff --git a/web/src/components/inputs/RadioFieldset.tsx b/web/src/components/inputs/RadioFieldset.tsx deleted file mode 100644 index 46a8598..0000000 --- a/web/src/components/inputs/RadioFieldset.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import {Field} from "react-final-form"; - -export interface radioFieldsetOption { - label: string; - description: string; - value: string; -} - -interface props { - name: string; - legend: string; - options: radioFieldsetOption[]; -} - -const RadioFieldset: React.FC = ({ name, legend,options }) => ( -
    -
    -
    - {legend} -
    -
    -
    - - {options.map((opt, idx) => ( -
    -
    - ( - - )} - /> -
    -
    - -

    - {opt.description} -

    -
    -
    - ))} - -
    -
    -
    -
    -) - -export default RadioFieldset; diff --git a/web/src/components/inputs/SwitchGroup.tsx b/web/src/components/inputs/SwitchGroup.tsx deleted file mode 100644 index af41fec..0000000 --- a/web/src/components/inputs/SwitchGroup.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from "react"; -import { Switch } from "@headlessui/react"; -import { Field } from "react-final-form"; -import { classNames } from "../../styles/utils"; - -interface Props { - name: string; - label: string; - description?: string; - defaultValue?: boolean; - className?: string; -} - -const SwitchGroup: React.FC = ({ name, label, description, defaultValue }) => ( -
      - -
      - - {label} - - {description && ( - - {description} - - )} -
      - - ( - - Use setting - - )} - /> -
      -
    -) - -export default SwitchGroup; \ No newline at end of file diff --git a/web/src/components/inputs/TextAreaWide.tsx b/web/src/components/inputs/TextAreaWide.tsx deleted file mode 100644 index 24402f5..0000000 --- a/web/src/components/inputs/TextAreaWide.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import {Field} from "react-final-form"; -import React from "react"; -import Error from "./Error"; -import {classNames} from "../../styles/utils"; - -interface Props { - name: string; - label?: string; - placeholder?: string; - className?: string; - required?: boolean; -} - -const TextAreaWide: React.FC = ({name, label, placeholder, required, className}) => ( -
    -
    - - -
    -
    - ( -