summaryrefslogtreecommitdiffhomepage
path: root/frontend/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/pages')
-rw-r--r--frontend/src/pages/404.tsx8
-rw-r--r--frontend/src/pages/Authentication.scss3
-rw-r--r--frontend/src/pages/Authentication.tsx22
-rw-r--r--frontend/src/pages/Blacklist/Movies/index.tsx13
-rw-r--r--frontend/src/pages/Blacklist/Movies/table.tsx9
-rw-r--r--frontend/src/pages/Blacklist/Series/index.tsx10
-rw-r--r--frontend/src/pages/Blacklist/Series/table.tsx9
-rw-r--r--frontend/src/pages/Episodes/components.tsx11
-rw-r--r--frontend/src/pages/Episodes/index.tsx66
-rw-r--r--frontend/src/pages/Episodes/table.tsx57
-rw-r--r--frontend/src/pages/History/Movies/index.tsx17
-rw-r--r--frontend/src/pages/History/Series/index.tsx21
-rw-r--r--frontend/src/pages/History/Statistics/index.tsx22
-rw-r--r--frontend/src/pages/History/Statistics/options.ts2
-rw-r--r--frontend/src/pages/LaunchError.tsx4
-rw-r--r--frontend/src/pages/Movies/Details/index.tsx74
-rw-r--r--frontend/src/pages/Movies/Details/table.tsx17
-rw-r--r--frontend/src/pages/Movies/Editor.tsx64
-rw-r--r--frontend/src/pages/Movies/index.tsx77
-rw-r--r--frontend/src/pages/Series/Editor.tsx64
-rw-r--r--frontend/src/pages/Series/index.tsx86
-rw-r--r--frontend/src/pages/Settings/General/index.tsx10
-rw-r--r--frontend/src/pages/Settings/General/options.ts2
-rw-r--r--frontend/src/pages/Settings/Languages/components.tsx48
-rw-r--r--frontend/src/pages/Settings/Languages/index.tsx17
-rw-r--r--frontend/src/pages/Settings/Languages/modal.tsx80
-rw-r--r--frontend/src/pages/Settings/Languages/options.ts2
-rw-r--r--frontend/src/pages/Settings/Languages/table.tsx57
-rw-r--r--frontend/src/pages/Settings/Notifications/components.tsx45
-rw-r--r--frontend/src/pages/Settings/Notifications/index.tsx8
-rw-r--r--frontend/src/pages/Settings/Providers/components.tsx41
-rw-r--r--frontend/src/pages/Settings/Providers/index.tsx8
-rw-r--r--frontend/src/pages/Settings/Radarr/index.tsx12
-rw-r--r--frontend/src/pages/Settings/Scheduler/index.tsx9
-rw-r--r--frontend/src/pages/Settings/Scheduler/options.ts2
-rw-r--r--frontend/src/pages/Settings/Sonarr/index.tsx12
-rw-r--r--frontend/src/pages/Settings/Subtitles/index.tsx8
-rw-r--r--frontend/src/pages/Settings/Subtitles/options.ts2
-rw-r--r--frontend/src/pages/Settings/UI/index.tsx10
-rw-r--r--frontend/src/pages/Settings/UI/options.ts2
-rw-r--r--frontend/src/pages/Settings/components/Layout.tsx (renamed from frontend/src/pages/Settings/components/provider.tsx)40
-rw-r--r--frontend/src/pages/Settings/components/collapse.tsx14
-rw-r--r--frontend/src/pages/Settings/components/container.tsx3
-rw-r--r--frontend/src/pages/Settings/components/forms.tsx35
-rw-r--r--frontend/src/pages/Settings/components/hooks.ts14
-rw-r--r--frontend/src/pages/Settings/components/index.tsx8
-rw-r--r--frontend/src/pages/Settings/components/pathMapper.tsx109
-rw-r--r--frontend/src/pages/Settings/components/style.scss14
-rw-r--r--frontend/src/pages/Settings/options.ts2
-rw-r--r--frontend/src/pages/System/Backups/BackupDeleteModal.tsx19
-rw-r--r--frontend/src/pages/System/Backups/BackupRestoreModal.tsx19
-rw-r--r--frontend/src/pages/System/Backups/index.tsx8
-rw-r--r--frontend/src/pages/System/Backups/table.tsx13
-rw-r--r--frontend/src/pages/System/Logs/index.tsx12
-rw-r--r--frontend/src/pages/System/Logs/modal.tsx13
-rw-r--r--frontend/src/pages/System/Logs/table.tsx24
-rw-r--r--frontend/src/pages/System/Providers/index.tsx10
-rw-r--r--frontend/src/pages/System/Providers/table.tsx4
-rw-r--r--frontend/src/pages/System/Releases/index.tsx16
-rw-r--r--frontend/src/pages/System/Status/index.tsx21
-rw-r--r--frontend/src/pages/System/Status/style.scss3
-rw-r--r--frontend/src/pages/System/Status/table.tsx4
-rw-r--r--frontend/src/pages/System/Tasks/index.tsx10
-rw-r--r--frontend/src/pages/System/Tasks/table.tsx6
-rw-r--r--frontend/src/pages/UIError.tsx6
-rw-r--r--frontend/src/pages/Wanted/Movies/index.tsx21
-rw-r--r--frontend/src/pages/Wanted/Series/index.tsx21
67 files changed, 793 insertions, 707 deletions
diff --git a/frontend/src/pages/404.tsx b/frontend/src/pages/404.tsx
index 2d0033523..12fe005c7 100644
--- a/frontend/src/pages/404.tsx
+++ b/frontend/src/pages/404.tsx
@@ -1,11 +1,9 @@
import { faEyeSlash as fasEyeSlash } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Container } from "react-bootstrap";
-export const RouterEmptyPath = "/empty-page";
-
-const EmptyPage: FunctionComponent = () => {
+const NotFound: FunctionComponent = () => {
return (
<Container className="d-flex flex-column align-items-center my-5">
<h1>
@@ -17,4 +15,4 @@ const EmptyPage: FunctionComponent = () => {
);
};
-export default EmptyPage;
+export default NotFound;
diff --git a/frontend/src/pages/Authentication.scss b/frontend/src/pages/Authentication.scss
deleted file mode 100644
index 26b2bb602..000000000
--- a/frontend/src/pages/Authentication.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.auth-card {
- width: 24rem;
-}
diff --git a/frontend/src/pages/Authentication.tsx b/frontend/src/pages/Authentication.tsx
index 4b2ff721b..085ff6cc1 100644
--- a/frontend/src/pages/Authentication.tsx
+++ b/frontend/src/pages/Authentication.tsx
@@ -1,23 +1,21 @@
-import { useReduxStore } from "@redux/hooks/base";
-import logo from "@static/logo128.png";
-import { useSystem } from "apis/hooks";
-import React, { FunctionComponent, useState } from "react";
+import { useSystem } from "@/apis/hooks";
+import { useReduxStore } from "@/modules/redux/hooks/base";
+import { FunctionComponent, useState } from "react";
import { Button, Card, Form, Image, Spinner } from "react-bootstrap";
-import { Redirect } from "react-router-dom";
-import "./Authentication.scss";
+import { Navigate } from "react-router-dom";
-interface Props {}
-
-const Authentication: FunctionComponent<Props> = () => {
+const Authentication: FunctionComponent = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const { login, isWorking } = useSystem();
- const authenticated = useReduxStore((s) => s.status !== "unauthenticated");
+ const authenticated = useReduxStore(
+ (s) => s.site.status !== "unauthenticated"
+ );
if (authenticated) {
- return <Redirect to="/"></Redirect>;
+ return <Navigate to="/"></Navigate>;
}
return (
@@ -31,7 +29,7 @@ const Authentication: FunctionComponent<Props> = () => {
>
<Card.Body>
<Form.Group className="mb-5 d-flex justify-content-center">
- <Image width="64" height="64" src={logo}></Image>
+ <Image width="64" height="64" src="/static/logo128.png"></Image>
</Form.Group>
<Form.Group>
<Form.Control
diff --git a/frontend/src/pages/Blacklist/Movies/index.tsx b/frontend/src/pages/Blacklist/Movies/index.tsx
index d2eb9b8cd..063dab308 100644
--- a/frontend/src/pages/Blacklist/Movies/index.tsx
+++ b/frontend/src/pages/Blacklist/Movies/index.tsx
@@ -1,14 +1,15 @@
+import {
+ useMovieBlacklist,
+ useMovieDeleteBlacklist,
+} from "@/apis/hooks/movies";
+import { ContentHeader, QueryOverlay } from "@/components";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
-import { useMovieBlacklist, useMovieDeleteBlacklist } from "apis/hooks/movies";
-import { ContentHeader, QueryOverlay } from "components";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import Table from "./table";
-interface Props {}
-
-const BlacklistMoviesView: FunctionComponent<Props> = () => {
+const BlacklistMoviesView: FunctionComponent = () => {
const blacklist = useMovieBlacklist();
const { data } = blacklist;
diff --git a/frontend/src/pages/Blacklist/Movies/table.tsx b/frontend/src/pages/Blacklist/Movies/table.tsx
index d7d6df75f..d87740d7b 100644
--- a/frontend/src/pages/Blacklist/Movies/table.tsx
+++ b/frontend/src/pages/Blacklist/Movies/table.tsx
@@ -1,8 +1,9 @@
+import { useMovieDeleteBlacklist } from "@/apis/hooks";
+import { AsyncButton, PageTable, TextPopover } from "@/components";
+import Language from "@/components/bazarr/Language";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useMovieDeleteBlacklist } from "apis/hooks";
-import { AsyncButton, LanguageText, PageTable, TextPopover } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
@@ -31,7 +32,7 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
accessor: "language",
Cell: ({ value }) => {
if (value) {
- return <LanguageText text={value} long></LanguageText>;
+ return <Language.Text value={value} long></Language.Text>;
} else {
return null;
}
diff --git a/frontend/src/pages/Blacklist/Series/index.tsx b/frontend/src/pages/Blacklist/Series/index.tsx
index 07870c747..b9441f808 100644
--- a/frontend/src/pages/Blacklist/Series/index.tsx
+++ b/frontend/src/pages/Blacklist/Series/index.tsx
@@ -1,14 +1,12 @@
+import { useEpisodeBlacklist, useEpisodeDeleteBlacklist } from "@/apis/hooks";
+import { ContentHeader, QueryOverlay } from "@/components";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
-import { useEpisodeBlacklist, useEpisodeDeleteBlacklist } from "apis/hooks";
-import { ContentHeader, QueryOverlay } from "components";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import Table from "./table";
-interface Props {}
-
-const BlacklistSeriesView: FunctionComponent<Props> = () => {
+const BlacklistSeriesView: FunctionComponent = () => {
const blacklist = useEpisodeBlacklist();
const { mutateAsync } = useEpisodeDeleteBlacklist();
diff --git a/frontend/src/pages/Blacklist/Series/table.tsx b/frontend/src/pages/Blacklist/Series/table.tsx
index 0c522cb4f..43a86959b 100644
--- a/frontend/src/pages/Blacklist/Series/table.tsx
+++ b/frontend/src/pages/Blacklist/Series/table.tsx
@@ -1,8 +1,9 @@
+import { useEpisodeDeleteBlacklist } from "@/apis/hooks";
+import { AsyncButton, PageTable, TextPopover } from "@/components";
+import Language from "@/components/bazarr/Language";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useEpisodeDeleteBlacklist } from "apis/hooks";
-import { AsyncButton, LanguageText, PageTable, TextPopover } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
@@ -38,7 +39,7 @@ const Table: FunctionComponent<Props> = ({ blacklist }) => {
accessor: "language",
Cell: ({ value }) => {
if (value) {
- return <LanguageText text={value} long></LanguageText>;
+ return <Language.Text value={value} long></Language.Text>;
} else {
return null;
}
diff --git a/frontend/src/pages/Episodes/components.tsx b/frontend/src/pages/Episodes/components.tsx
index eb440fb82..7e6962699 100644
--- a/frontend/src/pages/Episodes/components.tsx
+++ b/frontend/src/pages/Episodes/components.tsx
@@ -1,8 +1,9 @@
+import { useEpisodeSubtitleModification } from "@/apis/hooks";
+import { AsyncButton } from "@/components";
+import Language from "@/components/bazarr/Language";
import { faSearch, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useEpisodeSubtitleModification } from "apis/hooks";
-import { AsyncButton, LanguageText } from "components";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Badge } from "react-bootstrap";
interface Props {
@@ -52,7 +53,7 @@ export const SubtitleAction: FunctionComponent<Props> = ({
className="mr-1"
variant={missing ? "primary" : "secondary"}
>
- <LanguageText className="pr-1" text={subtitle}></LanguageText>
+ <Language.Text className="pr-1" value={subtitle}></Language.Text>
<FontAwesomeIcon
size="sm"
icon={missing ? faSearch : faTrash}
@@ -62,7 +63,7 @@ export const SubtitleAction: FunctionComponent<Props> = ({
} else {
return (
<Badge className="mr-1" variant="secondary">
- <LanguageText text={subtitle} long={false}></LanguageText>
+ <Language.Text value={subtitle} long={false}></Language.Text>
</Badge>
);
}
diff --git a/frontend/src/pages/Episodes/index.tsx b/frontend/src/pages/Episodes/index.tsx
index dacc4a47c..a4efa7c4f 100644
--- a/frontend/src/pages/Episodes/index.tsx
+++ b/frontend/src/pages/Episodes/index.tsx
@@ -1,46 +1,38 @@
import {
- faAdjust,
- faBriefcase,
- faCloudUploadAlt,
- faHdd,
- faSearch,
- faSync,
- faWrench,
-} from "@fortawesome/free-solid-svg-icons";
-import { dispatchTask } from "@modules/task";
-import { createTask } from "@modules/task/utilities";
-import {
useEpisodesBySeriesId,
useIsAnyActionRunning,
useSeriesAction,
useSeriesById,
useSeriesModification,
-} from "apis/hooks";
+} from "@/apis/hooks";
import {
ContentHeader,
ItemEditorModal,
LoadingIndicator,
SeriesUploadModal,
- useShowModal,
-} from "components";
-import ItemOverview from "components/ItemOverview";
-import { RouterEmptyPath } from "pages/404";
-import React, { FunctionComponent, useMemo } from "react";
+} from "@/components";
+import ItemOverview from "@/components/ItemOverview";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { createAndDispatchTask } from "@/modules/task/utilities";
+import { useLanguageProfileBy } from "@/utilities/languages";
+import {
+ faAdjust,
+ faBriefcase,
+ faCloudUploadAlt,
+ faHdd,
+ faSearch,
+ faSync,
+ faWrench,
+} from "@fortawesome/free-solid-svg-icons";
+import { FunctionComponent, useMemo } from "react";
import { Alert, Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
-import { Redirect, RouteComponentProps, withRouter } from "react-router-dom";
-import { useLanguageProfileBy } from "utilities/languages";
+import { Navigate, useParams } from "react-router-dom";
import Table from "./table";
-interface Params {
- id: string;
-}
-
-interface Props extends RouteComponentProps<Params> {}
-
-const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
- const { match } = props;
- const id = Number.parseInt(match.params.id);
+const SeriesEpisodesView: FunctionComponent = () => {
+ const params = useParams();
+ const id = Number.parseInt(params.id as string);
const { data: series, isFetched } = useSeriesById(id);
const { data: episodes } = useEpisodesBySeriesId(id);
@@ -63,14 +55,14 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
[series]
);
- const showModal = useShowModal();
+ const { show } = useModalControl();
const profile = useLanguageProfileBy(series?.profileId);
const hasTask = useIsAnyActionRunning();
if (isNaN(id) || (isFetched && !series)) {
- return <Redirect to={RouterEmptyPath}></Redirect>;
+ return <Navigate to="/not-found"></Navigate>;
}
if (!series) {
@@ -88,11 +80,10 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
icon={faSync}
disabled={!available || hasTask}
onClick={() => {
- const task = createTask(series.title, id, action, {
+ createAndDispatchTask(series.title, "scan-disk", action, {
action: "scan-disk",
seriesid: id,
});
- dispatchTask("Scanning disk...", [task], "Scanning...");
}}
>
Scan Disk
@@ -100,11 +91,10 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
<ContentHeader.Button
icon={faSearch}
onClick={() => {
- const task = createTask(series.title, id, action, {
+ createAndDispatchTask(series.title, "search-subtitles", action, {
action: "search-missing",
seriesid: id,
});
- dispatchTask("Searching subtitles...", [task], "Searching...");
}}
disabled={
series.episodeFileCount === 0 ||
@@ -119,7 +109,7 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
<ContentHeader.Button
disabled={series.episodeFileCount === 0 || !available || hasTask}
icon={faBriefcase}
- onClick={() => showModal("tools", episodes)}
+ onClick={() => show("tools", episodes)}
>
Tools
</ContentHeader.Button>
@@ -130,14 +120,14 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
!available
}
icon={faCloudUploadAlt}
- onClick={() => showModal("upload", series)}
+ onClick={() => show("upload", series)}
>
Upload
</ContentHeader.Button>
<ContentHeader.Button
icon={faWrench}
disabled={hasTask}
- onClick={() => showModal("edit", series)}
+ onClick={() => show("edit", series)}
>
Edit Series
</ContentHeader.Button>
@@ -177,4 +167,4 @@ const SeriesEpisodesView: FunctionComponent<Props> = (props) => {
);
};
-export default withRouter(SeriesEpisodesView);
+export default SeriesEpisodesView;
diff --git a/frontend/src/pages/Episodes/table.tsx b/frontend/src/pages/Episodes/table.tsx
index 7b5582e6f..6da19a7f2 100644
--- a/frontend/src/pages/Episodes/table.tsx
+++ b/frontend/src/pages/Episodes/table.tsx
@@ -1,3 +1,16 @@
+import { useDownloadEpisodeSubtitles } from "@/apis/hooks";
+import {
+ ActionButton,
+ EpisodeHistoryModal,
+ GroupTable,
+ SubtitleToolModal,
+ TextPopover,
+} from "@/components";
+import { ManualSearchModal } from "@/components/modals/ManualSearchModal";
+import { useShowOnlyDesired } from "@/modules/redux/hooks";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { BuildKey, filterSubtitleBy } from "@/utilities";
+import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import {
faBookmark,
@@ -6,22 +19,9 @@ import {
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useShowOnlyDesired } from "@redux/hooks";
-import { useDownloadEpisodeSubtitles } from "apis/hooks";
-import {
- ActionButton,
- EpisodeHistoryModal,
- GroupTable,
- SubtitleToolModal,
- TextPopover,
- useShowModal,
-} from "components";
-import { ManualSearchModal } from "components/modals/ManualSearchModal";
-import React, { FunctionComponent, useCallback, useMemo } from "react";
+import { FunctionComponent, useCallback, useMemo } from "react";
import { Badge, ButtonGroup } from "react-bootstrap";
-import { Column, TableUpdater } from "react-table";
-import { BuildKey, filterSubtitleBy } from "utilities";
-import { useProfileItemsToLanguages } from "utilities/languages";
+import { Column } from "react-table";
import { SubtitleAction } from "./components";
interface Props {
@@ -37,8 +37,6 @@ const Table: FunctionComponent<Props> = ({
profile,
disabled,
}) => {
- const showModal = useShowModal();
-
const onlyDesired = useShowOnlyDesired();
const profileItems = useProfileItemsToLanguages(profile);
@@ -160,28 +158,29 @@ const Table: FunctionComponent<Props> = ({
{
Header: "Actions",
accessor: "sonarrEpisodeId",
- Cell: ({ row, update }) => {
+ Cell: ({ row }) => {
+ const { show } = useModalControl();
return (
<ButtonGroup>
<ActionButton
icon={faUser}
disabled={series?.profileId === null || disabled}
onClick={() => {
- update && update(row, "manual-search");
+ show("manual-search", row.original);
}}
></ActionButton>
<ActionButton
icon={faHistory}
disabled={disabled}
onClick={() => {
- update && update(row, "history");
+ show("manual-search", row.original);
}}
></ActionButton>
<ActionButton
icon={faBriefcase}
disabled={disabled}
onClick={() => {
- update && update(row, "tools");
+ show("tools", [row.original]);
}}
></ActionButton>
</ButtonGroup>
@@ -192,17 +191,6 @@ const Table: FunctionComponent<Props> = ({
[onlyDesired, profileItems, series, disabled]
);
- const updateRow = useCallback<TableUpdater<Item.Episode>>(
- (row, modalKey: string) => {
- if (modalKey === "tools") {
- showModal(modalKey, [row.original]);
- } else {
- showModal(modalKey, row.original);
- }
- },
- [showModal]
- );
-
const maxSeason = useMemo(
() =>
episodes.reduce<number>((prev, curr) => Math.max(prev, curr.season), 0),
@@ -210,11 +198,10 @@ const Table: FunctionComponent<Props> = ({
);
return (
- <React.Fragment>
+ <>
<GroupTable
columns={columns}
data={episodes}
- update={updateRow}
initialState={{
sortBy: [
{ id: "season", desc: true },
@@ -233,7 +220,7 @@ const Table: FunctionComponent<Props> = ({
modalKey="manual-search"
download={download}
></ManualSearchModal>
- </React.Fragment>
+ </>
);
};
diff --git a/frontend/src/pages/History/Movies/index.tsx b/frontend/src/pages/History/Movies/index.tsx
index de2ce0098..cbfaba78f 100644
--- a/frontend/src/pages/History/Movies/index.tsx
+++ b/frontend/src/pages/History/Movies/index.tsx
@@ -1,17 +1,16 @@
+import { useMovieAddBlacklist, useMovieHistoryPagination } from "@/apis/hooks";
+import { HistoryIcon, TextPopover } from "@/components";
+import Language from "@/components/bazarr/Language";
+import { BlacklistButton } from "@/components/inputs/blacklist";
+import HistoryView from "@/components/views/HistoryView";
import { faInfoCircle, faRecycle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useMovieAddBlacklist, useMovieHistoryPagination } from "apis/hooks";
-import { HistoryIcon, LanguageText, TextPopover } from "components";
-import { BlacklistButton } from "components/inputs/blacklist";
-import HistoryView from "components/views/HistoryView";
-import React, { FunctionComponent, useMemo } from "react";
+import { FunctionComponent, useMemo } from "react";
import { Badge, OverlayTrigger, Popover } from "react-bootstrap";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-interface Props {}
-
-const MoviesHistoryView: FunctionComponent<Props> = () => {
+const MoviesHistoryView: FunctionComponent = () => {
const columns: Column<History.Movie>[] = useMemo<Column<History.Movie>[]>(
() => [
{
@@ -40,7 +39,7 @@ const MoviesHistoryView: FunctionComponent<Props> = () => {
if (value) {
return (
<Badge variant="secondary">
- <LanguageText text={value} long></LanguageText>
+ <Language.Text value={value} long></Language.Text>
</Badge>
);
} else {
diff --git a/frontend/src/pages/History/Series/index.tsx b/frontend/src/pages/History/Series/index.tsx
index 143f360b8..e64e798d7 100644
--- a/frontend/src/pages/History/Series/index.tsx
+++ b/frontend/src/pages/History/Series/index.tsx
@@ -1,20 +1,19 @@
-import { faInfoCircle, faRecycle } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useEpisodeAddBlacklist,
useEpisodeHistoryPagination,
-} from "apis/hooks";
-import { HistoryIcon, LanguageText, TextPopover } from "components";
-import { BlacklistButton } from "components/inputs/blacklist";
-import HistoryView from "components/views/HistoryView";
-import React, { FunctionComponent, useMemo } from "react";
+} from "@/apis/hooks";
+import { HistoryIcon, TextPopover } from "@/components";
+import Language from "@/components/bazarr/Language";
+import { BlacklistButton } from "@/components/inputs/blacklist";
+import HistoryView from "@/components/views/HistoryView";
+import { faInfoCircle, faRecycle } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FunctionComponent, useMemo } from "react";
import { Badge, OverlayTrigger, Popover } from "react-bootstrap";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-interface Props {}
-
-const SeriesHistoryView: FunctionComponent<Props> = () => {
+const SeriesHistoryView: FunctionComponent = () => {
const columns: Column<History.Episode>[] = useMemo<Column<History.Episode>[]>(
() => [
{
@@ -50,7 +49,7 @@ const SeriesHistoryView: FunctionComponent<Props> = () => {
if (value) {
return (
<Badge variant="secondary">
- <LanguageText text={value} long></LanguageText>
+ <Language.Text value={value} long></Language.Text>
</Badge>
);
} else {
diff --git a/frontend/src/pages/History/Statistics/index.tsx b/frontend/src/pages/History/Statistics/index.tsx
index e1875c2f1..aee2c0b54 100644
--- a/frontend/src/pages/History/Statistics/index.tsx
+++ b/frontend/src/pages/History/Statistics/index.tsx
@@ -1,12 +1,13 @@
-import { useHistoryStats, useLanguages, useSystemProviders } from "apis/hooks";
+import { useHistoryStats, useSystemProviders } from "@/apis/hooks";
import {
ContentHeader,
- LanguageSelector,
QueryOverlay,
Selector,
-} from "components";
+ SelectorOption,
+} from "@/components";
+import Language from "@/components/bazarr/Language";
import { merge } from "lodash";
-import React, { FunctionComponent, useMemo, useState } from "react";
+import { FunctionComponent, useMemo, useState } from "react";
import { Col, Container } from "react-bootstrap";
import { Helmet } from "react-helmet";
import {
@@ -28,8 +29,6 @@ const SelectorContainer: FunctionComponent = ({ children }) => (
);
const HistoryStats: FunctionComponent = () => {
- const { data: languages } = useLanguages(true);
-
const { data: providers } = useSystemProviders(true);
const providerOptions = useMemo<SelectorOption<System.Provider>[]>(
@@ -63,13 +62,12 @@ const HistoryStats: FunctionComponent = () => {
}, [data]);
return (
- // TODO: Responsive
<Container fluid className="vh-75">
<Helmet>
<title>History Statistics - Bazarr</title>
</Helmet>
<QueryOverlay result={stats}>
- <React.Fragment>
+ <div className="chart-container">
<ContentHeader scroll={false}>
<SelectorContainer>
<Selector
@@ -98,12 +96,12 @@ const HistoryStats: FunctionComponent = () => {
></Selector>
</SelectorContainer>
<SelectorContainer>
- <LanguageSelector
+ <Language.Selector
clearable
- options={languages ?? []}
value={lang}
onChange={setLanguage}
- ></LanguageSelector>
+ history
+ ></Language.Selector>
</SelectorContainer>
</ContentHeader>
<ResponsiveContainer height="100%">
@@ -117,7 +115,7 @@ const HistoryStats: FunctionComponent = () => {
<Bar name="Movies" dataKey="movies" fill="#FFC22F"></Bar>
</BarChart>
</ResponsiveContainer>
- </React.Fragment>
+ </div>
</QueryOverlay>
</Container>
);
diff --git a/frontend/src/pages/History/Statistics/options.ts b/frontend/src/pages/History/Statistics/options.ts
index 49d2bbe59..88e36e85e 100644
--- a/frontend/src/pages/History/Statistics/options.ts
+++ b/frontend/src/pages/History/Statistics/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const actionOptions: SelectorOption<History.ActionOptions>[] = [
{
label: "Automatically Downloaded",
diff --git a/frontend/src/pages/LaunchError.tsx b/frontend/src/pages/LaunchError.tsx
index 80633e926..910b01ccd 100644
--- a/frontend/src/pages/LaunchError.tsx
+++ b/frontend/src/pages/LaunchError.tsx
@@ -1,8 +1,8 @@
+import { Reload } from "@/utilities";
import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Alert, Button, Container } from "react-bootstrap";
-import { Reload } from "utilities";
interface Props {
children: string;
diff --git a/frontend/src/pages/Movies/Details/index.tsx b/frontend/src/pages/Movies/Details/index.tsx
index 82a053111..842ceb025 100644
--- a/frontend/src/pages/Movies/Details/index.tsx
+++ b/frontend/src/pages/Movies/Details/index.tsx
@@ -1,20 +1,12 @@
import {
- faCloudUploadAlt,
- faHistory,
- faSearch,
- faSync,
- faToolbox,
- faUser,
- faWrench,
-} from "@fortawesome/free-solid-svg-icons";
-import { dispatchTask } from "@modules/task";
-import { createTask } from "@modules/task/utilities";
-import { useDownloadMovieSubtitles, useIsMovieActionRunning } from "apis/hooks";
+ useDownloadMovieSubtitles,
+ useIsMovieActionRunning,
+} from "@/apis/hooks";
import {
useMovieAction,
useMovieById,
useMovieModification,
-} from "apis/hooks/movies";
+} from "@/apis/hooks/movies";
import {
ContentHeader,
ItemEditorModal,
@@ -22,31 +14,35 @@ import {
MovieHistoryModal,
MovieUploadModal,
SubtitleToolModal,
- useShowModal,
-} from "components";
-import ItemOverview from "components/ItemOverview";
-import { ManualSearchModal } from "components/modals/ManualSearchModal";
-import { RouterEmptyPath } from "pages/404";
-import React, { FunctionComponent, useCallback } from "react";
+} from "@/components";
+import ItemOverview from "@/components/ItemOverview";
+import { ManualSearchModal } from "@/components/modals/ManualSearchModal";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { createAndDispatchTask } from "@/modules/task/utilities";
+import { useLanguageProfileBy } from "@/utilities/languages";
+import {
+ faCloudUploadAlt,
+ faHistory,
+ faSearch,
+ faSync,
+ faToolbox,
+ faUser,
+ faWrench,
+} from "@fortawesome/free-solid-svg-icons";
+import { FunctionComponent, useCallback } from "react";
import { Alert, Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
-import { Redirect, RouteComponentProps, withRouter } from "react-router-dom";
-import { useLanguageProfileBy } from "utilities/languages";
+import { Navigate, useParams } from "react-router-dom";
import Table from "./table";
-interface Params {
- id: string;
-}
-
-interface Props extends RouteComponentProps<Params> {}
-
-const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
- const id = Number.parseInt(match.params.id);
+const MovieDetailView: FunctionComponent = () => {
+ const param = useParams();
+ const id = Number.parseInt(param.id ?? "");
const { data: movie, isFetched } = useMovieById(id);
const profile = useLanguageProfileBy(movie?.profileId);
- const showModal = useShowModal();
+ const { show } = useModalControl();
const mutation = useMovieModification();
const { mutateAsync: action } = useMovieAction();
@@ -82,7 +78,7 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
const hasTask = useIsMovieActionRunning();
if (isNaN(id) || (isFetched && !movie)) {
- return <Redirect to={RouterEmptyPath}></Redirect>;
+ return <Navigate to="/not-found"></Navigate>;
}
if (!movie) {
@@ -102,11 +98,10 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
icon={faSync}
disabled={hasTask}
onClick={() => {
- const task = createTask(movie.title, id, action, {
+ createAndDispatchTask(movie.title, "scan-disk", action, {
action: "scan-disk",
radarrid: id,
});
- dispatchTask("Scanning Disk...", [task], "Scanning...");
}}
>
Scan Disk
@@ -115,11 +110,10 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
icon={faSearch}
disabled={movie.profileId === null}
onClick={() => {
- const task = createTask(movie.title, id, action, {
+ createAndDispatchTask(movie.title, "search-subtitles", action, {
action: "search-missing",
radarrid: id,
});
- dispatchTask("Searching subtitles...", [task], "Searching...");
}}
>
Search
@@ -127,20 +121,20 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
<ContentHeader.Button
icon={faUser}
disabled={movie.profileId === null || hasTask}
- onClick={() => showModal<Item.Movie>("manual-search", movie)}
+ onClick={() => show("manual-search", movie)}
>
Manual
</ContentHeader.Button>
<ContentHeader.Button
icon={faHistory}
- onClick={() => showModal("history", movie)}
+ onClick={() => show("history", movie)}
>
History
</ContentHeader.Button>
<ContentHeader.Button
icon={faToolbox}
disabled={hasTask}
- onClick={() => showModal("tools", [movie])}
+ onClick={() => show("tools", [movie])}
>
Tools
</ContentHeader.Button>
@@ -150,14 +144,14 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
<ContentHeader.Button
disabled={!allowEdit || movie.profileId === null || hasTask}
icon={faCloudUploadAlt}
- onClick={() => showModal("upload", movie)}
+ onClick={() => show("upload", movie)}
>
Upload
</ContentHeader.Button>
<ContentHeader.Button
icon={faWrench}
disabled={hasTask}
- onClick={() => showModal("edit", movie)}
+ onClick={() => show("edit", movie)}
>
Edit Movie
</ContentHeader.Button>
@@ -191,4 +185,4 @@ const MovieDetailView: FunctionComponent<Props> = ({ match }) => {
);
};
-export default withRouter(MovieDetailView);
+export default MovieDetailView;
diff --git a/frontend/src/pages/Movies/Details/table.tsx b/frontend/src/pages/Movies/Details/table.tsx
index 6187731c1..c87651e77 100644
--- a/frontend/src/pages/Movies/Details/table.tsx
+++ b/frontend/src/pages/Movies/Details/table.tsx
@@ -1,13 +1,14 @@
+import { useMovieSubtitleModification } from "@/apis/hooks";
+import { AsyncButton, SimpleTable } from "@/components";
+import Language from "@/components/bazarr/Language";
+import { useShowOnlyDesired } from "@/modules/redux/hooks";
+import { filterSubtitleBy } from "@/utilities";
+import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faSearch, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useShowOnlyDesired } from "@redux/hooks";
-import { useMovieSubtitleModification } from "apis/hooks";
-import { AsyncButton, LanguageText, SimpleTable } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { FunctionComponent, useMemo } from "react";
import { Badge } from "react-bootstrap";
import { Column } from "react-table";
-import { filterSubtitleBy } from "utilities";
-import { useProfileItemsToLanguages } from "utilities/languages";
const missingText = "Missing Subtitles";
@@ -44,13 +45,13 @@ const Table: FunctionComponent<Props> = ({ movie, profile, disabled }) => {
if (row.original.path === missingText) {
return (
<Badge variant="primary">
- <LanguageText text={row.original} long></LanguageText>
+ <Language.Text value={row.original} long></Language.Text>
</Badge>
);
} else {
return (
<Badge variant="secondary">
- <LanguageText text={row.original} long></LanguageText>
+ <Language.Text value={row.original} long></Language.Text>
</Badge>
);
}
diff --git a/frontend/src/pages/Movies/Editor.tsx b/frontend/src/pages/Movies/Editor.tsx
new file mode 100644
index 000000000..97b863a1f
--- /dev/null
+++ b/frontend/src/pages/Movies/Editor.tsx
@@ -0,0 +1,64 @@
+import { useMovieModification, useMovies } from "@/apis/hooks";
+import { QueryOverlay } from "@/components";
+import LanguageProfile from "@/components/bazarr/LanguageProfile";
+import MassEditor from "@/components/MassEditor";
+import { BuildKey } from "@/utilities";
+import { FunctionComponent, useMemo } from "react";
+import { Badge } from "react-bootstrap";
+import { Helmet } from "react-helmet";
+import { Column } from "react-table";
+
+const MovieMassEditor: FunctionComponent = () => {
+ const query = useMovies();
+ const mutation = useMovieModification();
+
+ const columns = useMemo<Column<Item.Movie>[]>(
+ () => [
+ {
+ Header: "Name",
+ accessor: "title",
+ className: "text-nowrap",
+ },
+ {
+ Header: "Audio",
+ accessor: "audio_language",
+ Cell: (row) => {
+ return row.value.map((v) => (
+ <Badge
+ variant="secondary"
+ className="mr-2"
+ key={BuildKey(v.code2, v.code2, v.hi)}
+ >
+ {v.name}
+ </Badge>
+ ));
+ },
+ },
+ {
+ Header: "Languages Profile",
+ accessor: "profileId",
+ Cell: ({ value }) => {
+ return <LanguageProfile index={value}></LanguageProfile>;
+ },
+ },
+ ],
+ []
+ );
+
+ return (
+ <QueryOverlay result={query}>
+ <>
+ <Helmet>
+ <title>Movies - Bazarr (Mass Editor)</title>
+ </Helmet>
+ <MassEditor
+ columns={columns}
+ data={query.data ?? []}
+ mutation={mutation}
+ ></MassEditor>
+ </>
+ </QueryOverlay>
+ );
+};
+
+export default MovieMassEditor;
diff --git a/frontend/src/pages/Movies/index.tsx b/frontend/src/pages/Movies/index.tsx
index b142c21b5..2939ee2a0 100644
--- a/frontend/src/pages/Movies/index.tsx
+++ b/frontend/src/pages/Movies/index.tsx
@@ -1,34 +1,28 @@
+import { useMovieModification, useMoviesPagination } from "@/apis/hooks";
+import { ActionBadge, ItemEditorModal, TextPopover } from "@/components";
+import Language from "@/components/bazarr/Language";
+import LanguageProfile from "@/components/bazarr/LanguageProfile";
+import ItemView from "@/components/views/ItemView";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { BuildKey } from "@/utilities";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import { faBookmark, faWrench } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import {
- useLanguageProfiles,
- useMovieModification,
- useMovies,
- useMoviesPagination,
-} from "apis/hooks";
-import { ActionBadge, LanguageText, TextPopover } from "components";
-import ItemView from "components/views/ItemView";
-import React, { FunctionComponent, useMemo } from "react";
-import { Badge } from "react-bootstrap";
+import { FunctionComponent, useMemo } from "react";
+import { Badge, Container } from "react-bootstrap";
+import { Helmet } from "react-helmet";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-import { BuildKey } from "utilities";
-interface Props {}
-
-const MovieView: FunctionComponent<Props> = () => {
- const { data: profiles } = useLanguageProfiles();
+const MovieView: FunctionComponent = () => {
const mutation = useMovieModification();
const query = useMoviesPagination();
- const full = useMovies();
const columns: Column<Item.Movie>[] = useMemo<Column<Item.Movie>[]>(
() => [
{
accessor: "monitored",
- selectHide: true,
Cell: ({ value }) => (
<FontAwesomeIcon
title={value ? "monitored" : "unmonitored"}
@@ -40,19 +34,15 @@ const MovieView: FunctionComponent<Props> = () => {
Header: "Name",
accessor: "title",
className: "text-nowrap",
- Cell: ({ row, value, isSelecting: select }) => {
- if (select) {
- return value;
- } else {
- const target = `/movies/${row.original.radarrId}`;
- return (
- <TextPopover text={row.original.sceneName} delay={1}>
- <Link to={target}>
- <span>{value}</span>
- </Link>
- </TextPopover>
- );
- }
+ Cell: ({ row, value }) => {
+ const target = `/movies/${row.original.radarrId}`;
+ return (
+ <TextPopover text={row.original.sceneName} delay={1}>
+ <Link to={target}>
+ <span>{value}</span>
+ </Link>
+ </TextPopover>
+ );
},
},
{
@@ -74,13 +64,12 @@ const MovieView: FunctionComponent<Props> = () => {
Header: "Languages Profile",
accessor: "profileId",
Cell: ({ value }) => {
- return profiles?.find((v) => v.profileId === value)?.name ?? null;
+ return <LanguageProfile index={value} empty=""></LanguageProfile>;
},
},
{
Header: "Missing Subtitles",
accessor: "missing_subtitles",
- selectHide: true,
Cell: (row) => {
const missing = row.value;
return missing.map((v) => (
@@ -89,35 +78,35 @@ const MovieView: FunctionComponent<Props> = () => {
variant="warning"
key={BuildKey(v.code2, v.hi, v.forced)}
>
- <LanguageText text={v}></LanguageText>
+ <Language.Text value={v}></Language.Text>
</Badge>
));
},
},
{
accessor: "radarrId",
- selectHide: true,
- Cell: ({ row, update }) => {
+ Cell: ({ row }) => {
+ const { show } = useModalControl();
return (
<ActionBadge
icon={faWrench}
- onClick={() => update && update(row, "edit")}
+ onClick={() => show("edit", row.original)}
></ActionBadge>
);
},
},
],
- [profiles]
+ []
);
return (
- <ItemView
- name="Movies"
- fullQuery={full}
- query={query}
- columns={columns}
- mutation={mutation}
- ></ItemView>
+ <Container fluid>
+ <Helmet>
+ <title>Movies - Bazarr</title>
+ </Helmet>
+ <ItemView query={query} columns={columns}></ItemView>
+ <ItemEditorModal modalKey="edit" mutation={mutation}></ItemEditorModal>
+ </Container>
);
};
diff --git a/frontend/src/pages/Series/Editor.tsx b/frontend/src/pages/Series/Editor.tsx
new file mode 100644
index 000000000..9cfc4e855
--- /dev/null
+++ b/frontend/src/pages/Series/Editor.tsx
@@ -0,0 +1,64 @@
+import { useSeries, useSeriesModification } from "@/apis/hooks";
+import { QueryOverlay } from "@/components";
+import LanguageProfile from "@/components/bazarr/LanguageProfile";
+import MassEditor from "@/components/MassEditor";
+import { BuildKey } from "@/utilities";
+import { FunctionComponent, useMemo } from "react";
+import { Badge } from "react-bootstrap";
+import { Helmet } from "react-helmet";
+import { Column } from "react-table";
+
+const SeriesMassEditor: FunctionComponent = () => {
+ const query = useSeries();
+ const mutation = useSeriesModification();
+
+ const columns = useMemo<Column<Item.Series>[]>(
+ () => [
+ {
+ Header: "Name",
+ accessor: "title",
+ className: "text-nowrap",
+ },
+ {
+ Header: "Audio",
+ accessor: "audio_language",
+ Cell: (row) => {
+ return row.value.map((v) => (
+ <Badge
+ variant="secondary"
+ className="mr-2"
+ key={BuildKey(v.code2, v.forced, v.hi)}
+ >
+ {v.name}
+ </Badge>
+ ));
+ },
+ },
+ {
+ Header: "Languages Profile",
+ accessor: "profileId",
+ Cell: ({ value }) => {
+ return <LanguageProfile index={value}></LanguageProfile>;
+ },
+ },
+ ],
+ []
+ );
+
+ return (
+ <QueryOverlay result={query}>
+ <>
+ <Helmet>
+ <title>Series - Bazarr (Mass Editor)</title>
+ </Helmet>
+ <MassEditor
+ columns={columns}
+ data={query.data ?? []}
+ mutation={mutation}
+ ></MassEditor>
+ </>
+ </QueryOverlay>
+ );
+};
+
+export default SeriesMassEditor;
diff --git a/frontend/src/pages/Series/index.tsx b/frontend/src/pages/Series/index.tsx
index bab2f1ba3..5f96f1d75 100644
--- a/frontend/src/pages/Series/index.tsx
+++ b/frontend/src/pages/Series/index.tsx
@@ -1,26 +1,20 @@
+import { useSeriesModification, useSeriesPagination } from "@/apis/hooks";
+import { ActionBadge, ItemEditorModal } from "@/components";
+import LanguageProfile from "@/components/bazarr/LanguageProfile";
+import ItemView from "@/components/views/ItemView";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { BuildKey } from "@/utilities";
import { faWrench } from "@fortawesome/free-solid-svg-icons";
-import {
- useLanguageProfiles,
- useSeries,
- useSeriesModification,
- useSeriesPagination,
-} from "apis/hooks";
-import { ActionBadge } from "components";
-import ItemView from "components/views/ItemView";
-import React, { FunctionComponent, useMemo } from "react";
-import { Badge, ProgressBar } from "react-bootstrap";
+import { FunctionComponent, useMemo } from "react";
+import { Badge, Container, ProgressBar } from "react-bootstrap";
+import { Helmet } from "react-helmet";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-import { BuildKey } from "utilities";
-interface Props {}
-
-const SeriesView: FunctionComponent<Props> = () => {
- const { data: profiles } = useLanguageProfiles();
+const SeriesView: FunctionComponent = () => {
const mutation = useSeriesModification();
const query = useSeriesPagination();
- const full = useSeries();
const columns: Column<Item.Series>[] = useMemo<Column<Item.Series>[]>(
() => [
@@ -28,17 +22,13 @@ const SeriesView: FunctionComponent<Props> = () => {
Header: "Name",
accessor: "title",
className: "text-nowrap",
- Cell: ({ row, value, isSelecting: select }) => {
- if (select) {
- return value;
- } else {
- const target = `/series/${row.original.sonarrSeriesId}`;
- return (
- <Link to={target}>
- <span>{value}</span>
- </Link>
- );
- }
+ Cell: ({ row, value }) => {
+ const target = `/series/${row.original.sonarrSeriesId}`;
+ return (
+ <Link to={target}>
+ <span>{value}</span>
+ </Link>
+ );
},
},
{
@@ -60,17 +50,15 @@ const SeriesView: FunctionComponent<Props> = () => {
Header: "Languages Profile",
accessor: "profileId",
Cell: ({ value }) => {
- return profiles?.find((v) => v.profileId === value)?.name ?? null;
+ return <LanguageProfile index={value} empty=""></LanguageProfile>;
},
},
{
Header: "Episodes",
accessor: "episodeFileCount",
- selectHide: true,
- Cell: ({ row }) => {
+ Cell: (row) => {
const { episodeFileCount, episodeMissingCount, profileId, title } =
- row.original;
-
+ row.row.original;
let progress = 0;
let label = "";
if (episodeFileCount === 0 || !profileId) {
@@ -99,28 +87,28 @@ const SeriesView: FunctionComponent<Props> = () => {
},
{
accessor: "sonarrSeriesId",
- selectHide: true,
- Cell: ({ row, update }) => (
- <ActionBadge
- icon={faWrench}
- onClick={() => {
- update && update(row, "edit");
- }}
- ></ActionBadge>
- ),
+ Cell: ({ row: { original } }) => {
+ const { show } = useModalControl();
+ return (
+ <ActionBadge
+ icon={faWrench}
+ onClick={() => show("edit", original)}
+ ></ActionBadge>
+ );
+ },
},
],
- [profiles]
+ []
);
return (
- <ItemView
- name="Series"
- fullQuery={full}
- query={query}
- columns={columns}
- mutation={mutation}
- ></ItemView>
+ <Container fluid>
+ <Helmet>
+ <title>Series - Bazarr</title>
+ </Helmet>
+ <ItemView query={query} columns={columns}></ItemView>
+ <ItemEditorModal modalKey="edit" mutation={mutation}></ItemEditorModal>
+ </Container>
);
};
diff --git a/frontend/src/pages/Settings/General/index.tsx b/frontend/src/pages/Settings/General/index.tsx
index 5e9b1b5dc..b5ec38dec 100644
--- a/frontend/src/pages/Settings/General/index.tsx
+++ b/frontend/src/pages/Settings/General/index.tsx
@@ -1,12 +1,12 @@
+import { copyToClipboard, Environment, toggleState } from "@/utilities";
import {
faCheck,
faClipboard,
faSync,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent, useState } from "react";
+import { FunctionComponent, useState } from "react";
import { InputGroup } from "react-bootstrap";
-import { copyToClipboard, Environment, toggleState } from "utilities";
import {
Button,
Check,
@@ -15,9 +15,9 @@ import {
File,
Group,
Input,
+ Layout,
Message,
Selector,
- SettingsProvider,
Text,
} from "../components";
import { branchOptions, proxyOptions, securityOptions } from "./options";
@@ -39,7 +39,7 @@ const SettingsGeneralView: FunctionComponent = () => {
const [copied, setCopy] = useState(false);
return (
- <SettingsProvider title="General - Bazarr (Settings)">
+ <Layout name="General">
<Group header="Host">
<Input name="Address">
<Text placeholder="0.0.0.0" settingKey="settings-general-ip"></Text>
@@ -209,7 +209,7 @@ const SettingsGeneralView: FunctionComponent = () => {
</Message>
</Input>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/General/options.ts b/frontend/src/pages/Settings/General/options.ts
index ee36eba53..77b1a63bc 100644
--- a/frontend/src/pages/Settings/General/options.ts
+++ b/frontend/src/pages/Settings/General/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const securityOptions: SelectorOption<string>[] = [
{
label: "Basic",
diff --git a/frontend/src/pages/Settings/Languages/components.tsx b/frontend/src/pages/Settings/Languages/components.tsx
index abfdba601..3fc262706 100644
--- a/frontend/src/pages/Settings/Languages/components.tsx
+++ b/frontend/src/pages/Settings/Languages/components.tsx
@@ -1,5 +1,8 @@
-import { LanguageSelector as CLanguageSelector } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import {
+ LanguageSelector as CLanguageSelector,
+ SelectorOption,
+} from "@/components";
+import { FunctionComponent, useMemo } from "react";
import { useLatestEnabledLanguages, useLatestProfiles } from ".";
import { BaseInput, Selector, useSingleUpdate } from "../components";
@@ -25,27 +28,24 @@ export const LanguageSelector: FunctionComponent<
);
};
-interface ProfileSelectorProps {}
+export const ProfileSelector: FunctionComponent<BaseInput<Language.Profile>> =
+ ({ settingKey }) => {
+ const profiles = useLatestProfiles();
-export const ProfileSelector: FunctionComponent<
- ProfileSelectorProps & BaseInput<Language.Profile>
-> = ({ settingKey }) => {
- const profiles = useLatestProfiles();
+ const profileOptions = useMemo<SelectorOption<number>[]>(
+ () =>
+ profiles.map((v) => {
+ return { label: v.name, value: v.profileId };
+ }),
+ [profiles]
+ );
- const profileOptions = useMemo<SelectorOption<number>[]>(
- () =>
- profiles.map((v) => {
- return { label: v.name, value: v.profileId };
- }),
- [profiles]
- );
-
- return (
- <Selector
- clearable
- options={profileOptions}
- settingKey={settingKey}
- beforeStaged={(v) => (v === null ? "" : v)}
- ></Selector>
- );
-};
+ return (
+ <Selector
+ clearable
+ options={profileOptions}
+ settingKey={settingKey}
+ beforeStaged={(v) => (v === null ? "" : v)}
+ ></Selector>
+ );
+ };
diff --git a/frontend/src/pages/Settings/Languages/index.tsx b/frontend/src/pages/Settings/Languages/index.tsx
index fd0473226..25b2b773d 100644
--- a/frontend/src/pages/Settings/Languages/index.tsx
+++ b/frontend/src/pages/Settings/Languages/index.tsx
@@ -1,14 +1,14 @@
-import { useLanguageProfiles, useLanguages } from "apis/hooks";
+import { useLanguageProfiles, useLanguages } from "@/apis/hooks";
+import { useEnabledLanguages } from "@/utilities/languages";
import { isArray } from "lodash";
-import React, { FunctionComponent } from "react";
-import { useEnabledLanguages } from "utilities/languages";
+import { FunctionComponent } from "react";
import {
Check,
CollapseBox,
Group,
Input,
+ Layout,
Message,
- SettingsProvider,
useLatest,
} from "../components";
import { enabledLanguageKey, languageProfileKey } from "../keys";
@@ -37,13 +37,10 @@ export function useLatestProfiles() {
}
}
-interface Props {}
-
-const SettingsLanguagesView: FunctionComponent<Props> = () => {
+const SettingsLanguagesView: FunctionComponent = () => {
const { data: languages } = useLanguages();
-
return (
- <SettingsProvider title="Languages - Bazarr (Settings)">
+ <Layout name="Languages">
<Group header="Subtitles Language">
<Input>
<Check
@@ -108,7 +105,7 @@ const SettingsLanguagesView: FunctionComponent<Props> = () => {
</CollapseBox.Content>
</CollapseBox>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Languages/modal.tsx b/frontend/src/pages/Settings/Languages/modal.tsx
index 61935b251..86c319de9 100644
--- a/frontend/src/pages/Settings/Languages/modal.tsx
+++ b/frontend/src/pages/Settings/Languages/modal.tsx
@@ -1,4 +1,3 @@
-import { faTrash } from "@fortawesome/free-solid-svg-icons";
import {
ActionButton,
BaseModal,
@@ -6,22 +5,38 @@ import {
Chips,
LanguageSelector,
Selector,
+ SelectorOption,
SimpleTable,
- useModalInformation,
-} from "components";
-import React, {
+} from "@/components";
+import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
+import { BuildKey } from "@/utilities";
+import { LOG } from "@/utilities/console";
+import { faTrash } from "@fortawesome/free-solid-svg-icons";
+import {
+ createContext,
FunctionComponent,
useCallback,
+ useContext,
useEffect,
useMemo,
useState,
} from "react";
import { Button, Form } from "react-bootstrap";
-import { Column, TableUpdater } from "react-table";
-import { BuildKey } from "utilities";
+import { Column } from "react-table";
import { useLatestEnabledLanguages } from ".";
import { Input, Message } from "../components";
import { cutoffOptions } from "./options";
+
+type ModifyFn = (index: number, item?: Language.ProfileItem) => void;
+
+const RowContext = createContext<ModifyFn>(() => {
+ LOG("error", "RowContext not initialized");
+});
+
+function useRowMutation() {
+ return useContext(RowContext);
+}
+
interface Props {
update: (profile: Language.Profile) => void;
}
@@ -43,8 +58,9 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
) => {
const { update, ...modal } = props;
- const { payload: profile, closeModal } =
- useModalInformation<Language.Profile>(modal.modalKey);
+ const profile = usePayload<Language.Profile>(modal.modalKey);
+
+ const { hide } = useModalControl();
const languages = useLatestEnabledLanguages();
@@ -80,13 +96,13 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
[current]
);
- const updateRow = useCallback<TableUpdater<Language.ProfileItem>>(
- (row, item: Language.ProfileItem) => {
+ const mutateRow = useCallback(
+ (index: number, item?: Language.ProfileItem) => {
const list = [...current.items];
if (item) {
- list[row.index] = item;
+ list[index] = item;
} else {
- list.splice(row.index, 1);
+ list.splice(index, 1);
}
updateProfile("items", list);
},
@@ -122,7 +138,7 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
<Button
disabled={!canSave}
onClick={() => {
- closeModal();
+ hide();
update(current);
}}
>
@@ -139,13 +155,14 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
{
Header: "Language",
accessor: "language",
- Cell: ({ value, row, update }) => {
+ Cell: ({ value, row }) => {
const code = value;
const item = row.original;
const lang = useMemo(
() => languages.find((l) => l.code2 === code) ?? null,
[code]
);
+ const mutate = useRowMutation();
return (
<div style={{ width: "8rem" }}>
<LanguageSelector
@@ -154,7 +171,7 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
onChange={(l) => {
if (l) {
item.language = l.code2;
- update && update(row, item);
+ mutate(row.index, item);
}
}}
></LanguageSelector>
@@ -165,8 +182,9 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
{
Header: "Forced",
accessor: "forced",
- Cell: ({ row, value, update }) => {
+ Cell: ({ row, value }) => {
const item = row.original;
+ const mutate = useRowMutation();
return (
<Form.Check
custom
@@ -174,7 +192,7 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
checked={value === "True"}
onChange={(v) => {
item.forced = v.target.checked ? "True" : "False";
- update && update(row, item);
+ mutate(row.index, item);
}}
></Form.Check>
);
@@ -183,8 +201,9 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
{
Header: "HI",
accessor: "hi",
- Cell: ({ row, value, update }) => {
+ Cell: ({ row, value }) => {
const item = row.original;
+ const mutate = useRowMutation();
return (
<Form.Check
custom
@@ -192,7 +211,7 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
checked={value === "True"}
onChange={(v) => {
item.hi = v.target.checked ? "True" : "False";
- update && update(row, item);
+ mutate(row.index, item);
}}
></Form.Check>
);
@@ -201,8 +220,9 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
{
Header: "Exclude Audio",
accessor: "audio_exclude",
- Cell: ({ row, value, update }) => {
+ Cell: ({ row, value }) => {
const item = row.original;
+ const mutate = useRowMutation();
return (
<Form.Check
custom
@@ -210,7 +230,7 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
checked={value === "True"}
onChange={(v) => {
item.audio_exclude = v.target.checked ? "True" : "False";
- update && update(row, item);
+ mutate(row.index, item);
}}
></Form.Check>
);
@@ -219,11 +239,12 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
{
id: "action",
accessor: "id",
- Cell: ({ row, update }) => {
+ Cell: ({ row }) => {
+ const mutate = useRowMutation();
return (
<ActionButton
icon={faTrash}
- onClick={() => update && update(row)}
+ onClick={() => mutate(row.index)}
></ActionButton>
);
},
@@ -245,12 +266,13 @@ const LanguagesProfileModal: FunctionComponent<Props & BaseModalProps> = (
></Form.Control>
</Input>
<Input>
- <SimpleTable
- responsive={false}
- columns={columns}
- data={current.items}
- update={updateRow}
- ></SimpleTable>
+ <RowContext.Provider value={mutateRow}>
+ <SimpleTable
+ responsive={false}
+ columns={columns}
+ data={current.items}
+ ></SimpleTable>
+ </RowContext.Provider>
<Button block variant="light" onClick={addItem}>
Add
</Button>
diff --git a/frontend/src/pages/Settings/Languages/options.ts b/frontend/src/pages/Settings/Languages/options.ts
index 01e5da382..fc9021b79 100644
--- a/frontend/src/pages/Settings/Languages/options.ts
+++ b/frontend/src/pages/Settings/Languages/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const anyCutoff = 65535;
export const cutoffOptions: SelectorOption<number>[] = [
diff --git a/frontend/src/pages/Settings/Languages/table.tsx b/frontend/src/pages/Settings/Languages/table.tsx
index 13a8ea0b0..ed87274da 100644
--- a/frontend/src/pages/Settings/Languages/table.tsx
+++ b/frontend/src/pages/Settings/Languages/table.tsx
@@ -1,15 +1,33 @@
+import { ActionButton, SimpleTable } from "@/components";
+import { useModalControl } from "@/modules/redux/hooks/modal";
+import { LOG } from "@/utilities/console";
import { faTrash, faWrench } from "@fortawesome/free-solid-svg-icons";
-import { ActionButton, SimpleTable, useShowModal } from "components";
import { cloneDeep } from "lodash";
-import React, { FunctionComponent, useCallback, useMemo } from "react";
+import {
+ createContext,
+ FunctionComponent,
+ useCallback,
+ useContext,
+ useMemo,
+} from "react";
import { Badge, Button, ButtonGroup } from "react-bootstrap";
-import { Column, TableUpdater } from "react-table";
+import { Column } from "react-table";
import { useLatestEnabledLanguages, useLatestProfiles } from ".";
import { useSingleUpdate } from "../components";
import { languageProfileKey } from "../keys";
import Modal from "./modal";
import { anyCutoff } from "./options";
+type ModifyFn = (index: number, item?: Language.Profile) => void;
+
+const RowContext = createContext<ModifyFn>(() => {
+ LOG("error", "RowContext not initialized");
+});
+
+function useRowMutation() {
+ return useContext(RowContext);
+}
+
const Table: FunctionComponent = () => {
const profiles = useLatestProfiles();
@@ -24,7 +42,7 @@ const Table: FunctionComponent = () => {
const update = useSingleUpdate();
- const showModal = useShowModal();
+ const { show } = useModalControl();
const submitProfiles = useCallback(
(list: Language.Profile[]) => {
@@ -48,17 +66,17 @@ const Table: FunctionComponent = () => {
[profiles, submitProfiles]
);
- const updateRow = useCallback<TableUpdater<Language.Profile>>(
- (row, item?: Language.Profile) => {
+ const mutateRow = useCallback<ModifyFn>(
+ (index, item) => {
if (item) {
- showModal("profile", cloneDeep(item));
+ show("profile", cloneDeep(item));
} else {
const list = [...profiles];
- list.splice(row.index, 1);
+ list.splice(index, 1);
submitProfiles(list);
}
},
- [submitProfiles, showModal, profiles]
+ [show, profiles, submitProfiles]
);
const columns = useMemo<Column<Language.Profile>[]>(
@@ -122,20 +140,21 @@ const Table: FunctionComponent = () => {
},
{
accessor: "profileId",
- Cell: ({ row, update }) => {
+ Cell: ({ row }) => {
const profile = row.original;
+ const mutate = useRowMutation();
return (
<ButtonGroup>
<ActionButton
icon={faWrench}
onClick={() => {
- update && update(row, profile);
+ mutate(row.index, profile);
}}
></ActionButton>
<ActionButton
icon={faTrash}
- onClick={() => update && update(row)}
+ onClick={() => mutate(row.index)}
></ActionButton>
</ButtonGroup>
);
@@ -148,12 +167,10 @@ const Table: FunctionComponent = () => {
const canAdd = languages.length !== 0;
return (
- <React.Fragment>
- <SimpleTable
- columns={columns}
- data={profiles}
- update={updateRow}
- ></SimpleTable>
+ <>
+ <RowContext.Provider value={mutateRow}>
+ <SimpleTable columns={columns} data={profiles}></SimpleTable>
+ </RowContext.Provider>
<Button
block
disabled={!canAdd}
@@ -168,13 +185,13 @@ const Table: FunctionComponent = () => {
mustNotContain: [],
originalFormat: false,
};
- showModal("profile", profile);
+ show("profile", profile);
}}
>
{canAdd ? "Add New Profile" : "No Enabled Languages"}
</Button>
<Modal update={updateProfile} modalKey="profile"></Modal>
- </React.Fragment>
+ </>
);
};
diff --git a/frontend/src/pages/Settings/Notifications/components.tsx b/frontend/src/pages/Settings/Notifications/components.tsx
index fee5611ae..5b8ed2007 100644
--- a/frontend/src/pages/Settings/Notifications/components.tsx
+++ b/frontend/src/pages/Settings/Notifications/components.tsx
@@ -1,21 +1,15 @@
-import api from "apis/raw";
+import api from "@/apis/raw";
import {
AsyncButton,
BaseModal,
BaseModalProps,
Selector,
- useModalInformation,
- useOnModalShow,
- useShowModal,
-} from "components";
-import React, {
- FunctionComponent,
- useCallback,
- useMemo,
- useState,
-} from "react";
+ SelectorOption,
+} from "@/components";
+import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
+import { BuildKey } from "@/utilities";
+import { FunctionComponent, useCallback, useMemo, useState } from "react";
import { Button, Col, Container, Form, Row } from "react-bootstrap";
-import { BuildKey } from "utilities";
import { ColCard, useLatestArray, useUpdateArray } from "../components";
import { notificationsKey } from "../keys";
@@ -43,17 +37,12 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
"name"
);
- const { payload, closeModal } =
- useModalInformation<Settings.NotificationInfo>(modal.modalKey);
+ const payload = usePayload<Settings.NotificationInfo>(modal.modalKey);
+ const { hide } = useModalControl();
const [current, setCurrent] =
useState<Nullable<Settings.NotificationInfo>>(payload);
- useOnModalShow<Settings.NotificationInfo>(
- (p) => setCurrent(p),
- modal.modalKey
- );
-
const updateUrl = useCallback((url: string) => {
setCurrent((current) => {
if (current) {
@@ -72,7 +61,7 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
const footer = useMemo(
() => (
- <React.Fragment>
+ <>
<AsyncButton
className="mr-auto"
disabled={!canSave}
@@ -94,7 +83,7 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
if (current) {
update({ ...current, enabled: false });
}
- closeModal();
+ hide();
}}
>
Remove
@@ -105,14 +94,14 @@ const NotificationModal: FunctionComponent<ModalProps & BaseModalProps> = ({
if (current) {
update({ ...current, enabled: true });
}
- closeModal();
+ hide();
}}
>
Save
</Button>
- </React.Fragment>
+ </>
),
- [canSave, closeModal, current, update, payload]
+ [canSave, payload, current, hide, update]
);
const getLabel = useCallback((v: Settings.NotificationInfo) => v.name, []);
@@ -157,7 +146,7 @@ export const NotificationView: FunctionComponent = () => {
(s) => s.notifications.providers
);
- const showModal = useShowModal();
+ const { show } = useModalControl();
const elements = useMemo(() => {
return notifications
@@ -166,16 +155,16 @@ export const NotificationView: FunctionComponent = () => {
<ColCard
key={BuildKey(idx, v.name)}
header={v.name}
- onClick={() => showModal("notifications", v)}
+ onClick={() => show("notifications", v)}
></ColCard>
));
- }, [notifications, showModal]);
+ }, [notifications, show]);
return (
<Container fluid>
<Row>
{elements}{" "}
- <ColCard plus onClick={() => showModal("notifications")}></ColCard>
+ <ColCard plus onClick={() => show("notifications")}></ColCard>
</Row>
<NotificationModal
selections={notifications ?? []}
diff --git a/frontend/src/pages/Settings/Notifications/index.tsx b/frontend/src/pages/Settings/Notifications/index.tsx
index 06585183f..325950805 100644
--- a/frontend/src/pages/Settings/Notifications/index.tsx
+++ b/frontend/src/pages/Settings/Notifications/index.tsx
@@ -1,11 +1,11 @@
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Alert } from "react-bootstrap";
-import { Check, Group, Input, Message, SettingsProvider } from "../components";
+import { Check, Group, Input, Layout, Message } from "../components";
import { NotificationView } from "./components";
const SettingsNotificationsView: FunctionComponent = () => {
return (
- <SettingsProvider title="Notifications - Bazarr (Settings)">
+ <Layout name="Notifications">
<Alert variant="secondary">
Thanks to caronc for his work on{" "}
<a
@@ -42,7 +42,7 @@ const SettingsNotificationsView: FunctionComponent = () => {
</Message>
</Input>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Providers/components.tsx b/frontend/src/pages/Settings/Providers/components.tsx
index 81e270610..4af98d95c 100644
--- a/frontend/src/pages/Settings/Providers/components.tsx
+++ b/frontend/src/pages/Settings/Providers/components.tsx
@@ -1,12 +1,13 @@
import {
BaseModal,
Selector,
- useModalInformation,
- useOnModalShow,
- useShowModal,
-} from "components";
+ SelectorComponents,
+ SelectorOption,
+} from "@/components";
+import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
+import { BuildKey, isReactText } from "@/utilities";
import { capitalize, isArray, isBoolean } from "lodash";
-import React, {
+import {
FunctionComponent,
useCallback,
useEffect,
@@ -15,8 +16,6 @@ import React, {
} from "react";
import { Button, Col, Container, Row } from "react-bootstrap";
import { components } from "react-select";
-import { SelectComponents } from "react-select/dist/declarations/src/components";
-import { BuildKey, isReactText } from "utilities";
import {
Check,
ColCard,
@@ -34,13 +33,13 @@ const ProviderKey = "settings-general-enabled_providers";
export const ProviderView: FunctionComponent = () => {
const providers = useLatest<string[]>(ProviderKey, isArray);
- const showModal = useShowModal();
+ const { show } = useModalControl();
const select = useCallback(
(v?: ProviderInfo) => {
- showModal(ModalKey, v ?? null);
+ show(ModalKey, v ?? null);
},
- [showModal]
+ [show]
);
const cards = useMemo(() => {
@@ -78,7 +77,8 @@ export const ProviderView: FunctionComponent = () => {
};
export const ProviderModal: FunctionComponent = () => {
- const { payload, closeModal } = useModalInformation<ProviderInfo>(ModalKey);
+ const payload = usePayload<ProviderInfo>(ModalKey);
+ const { hide } = useModalControl();
const [staged, setChange] = useState<LooseObject>({});
@@ -88,8 +88,6 @@ export const ProviderModal: FunctionComponent = () => {
const [info, setInfo] = useState<Nullable<ProviderInfo>>(payload);
- useOnModalShow<ProviderInfo>((p) => setInfo(p), ModalKey);
-
const providers = useLatest<string[]>(ProviderKey, isArray);
const updateGlobal = useMultiUpdate();
@@ -101,10 +99,10 @@ export const ProviderModal: FunctionComponent = () => {
const newProviders = [...providers];
newProviders.splice(idx, 1);
updateGlobal({ [ProviderKey]: newProviders });
- closeModal();
+ hide();
}
}
- }, [payload, providers, updateGlobal, closeModal]);
+ }, [payload, providers, updateGlobal, hide]);
const addProvider = useCallback(() => {
if (info && providers) {
@@ -117,22 +115,22 @@ export const ProviderModal: FunctionComponent = () => {
}
updateGlobal(changes);
- closeModal();
+ hide();
}
- }, [info, providers, staged, closeModal, updateGlobal]);
+ }, [info, providers, staged, updateGlobal, hide]);
const canSave = info !== null;
const footer = useMemo(
() => (
- <React.Fragment>
+ <>
<Button hidden={!payload} variant="danger" onClick={deletePayload}>
Delete
</Button>
<Button disabled={!canSave} onClick={addProvider}>
Save
</Button>
- </React.Fragment>
+ </>
),
[canSave, payload, deletePayload, addProvider]
);
@@ -218,12 +216,11 @@ export const ProviderModal: FunctionComponent = () => {
}, [info]);
const selectorComponents = useMemo<
- Partial<SelectComponents<ProviderInfo, false, any>>
+ Partial<SelectorComponents<ProviderInfo, false>>
>(
() => ({
Option: ({ data, ...other }) => {
- const { label, value } =
- data as unknown as SelectorOption<ProviderInfo>;
+ const { label, value } = data;
return (
<components.Option data={data} {...other}>
{label}
diff --git a/frontend/src/pages/Settings/Providers/index.tsx b/frontend/src/pages/Settings/Providers/index.tsx
index a7b9ef171..7ea651f6f 100644
--- a/frontend/src/pages/Settings/Providers/index.tsx
+++ b/frontend/src/pages/Settings/Providers/index.tsx
@@ -1,17 +1,17 @@
-import React, { FunctionComponent } from "react";
-import { Group, Input, SettingsProvider } from "../components";
+import { FunctionComponent } from "react";
+import { Group, Input, Layout } from "../components";
import { ProviderModal, ProviderView } from "./components";
const SettingsProvidersView: FunctionComponent = () => {
return (
- <SettingsProvider title="Providers - Bazarr (Settings)">
+ <Layout name="Providers">
<Group header="Providers">
<Input>
<ProviderView></ProviderView>
</Input>
</Group>
<ProviderModal></ProviderModal>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Radarr/index.tsx b/frontend/src/pages/Settings/Radarr/index.tsx
index 4511134f7..7d0822a23 100644
--- a/frontend/src/pages/Settings/Radarr/index.tsx
+++ b/frontend/src/pages/Settings/Radarr/index.tsx
@@ -1,4 +1,4 @@
-import React, { FunctionComponent, useCallback } from "react";
+import { FunctionComponent, useCallback } from "react";
import { InputGroup } from "react-bootstrap";
import {
Check,
@@ -6,24 +6,22 @@ import {
CollapseBox,
Group,
Input,
+ Layout,
Message,
PathMappingTable,
- SettingsProvider,
Slider,
Text,
URLTestButton,
} from "../components";
import { moviesEnabledKey } from "../keys";
-interface Props {}
-
-const SettingsRadarrView: FunctionComponent<Props> = () => {
+const SettingsRadarrView: FunctionComponent = () => {
const baseUrlOverride = useCallback((settings: Settings) => {
return settings.radarr.base_url?.slice(1) ?? "";
}, []);
return (
- <SettingsProvider title="Radarr - Bazarr (Settings)">
+ <Layout name="Radarr">
<CollapseBox>
<CollapseBox.Control>
<Group header="Use Radarr">
@@ -93,7 +91,7 @@ const SettingsRadarrView: FunctionComponent<Props> = () => {
</Group>
</CollapseBox.Content>
</CollapseBox>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Scheduler/index.tsx b/frontend/src/pages/Settings/Scheduler/index.tsx
index 4c1936396..2455dacaf 100644
--- a/frontend/src/pages/Settings/Scheduler/index.tsx
+++ b/frontend/src/pages/Settings/Scheduler/index.tsx
@@ -1,12 +1,13 @@
-import React, { FunctionComponent, useMemo } from "react";
+import { SelectorOption } from "@/components";
+import { FunctionComponent, useMemo } from "react";
import {
Check,
CollapseBox,
Group,
Input,
+ Layout,
Message,
Selector,
- SettingsProvider,
} from "../components";
import {
backupOptions,
@@ -29,7 +30,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
}, []);
return (
- <SettingsProvider title="Scheduler - Bazarr (Settings)">
+ <Layout name="Scheduler">
<Group header="Sonarr/Radarr Sync">
<Input name="Update Series List from Sonarr">
<Selector
@@ -172,7 +173,7 @@ const SettingsSchedulerView: FunctionComponent = () => {
</CollapseBox.Content>
</CollapseBox>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Scheduler/options.ts b/frontend/src/pages/Settings/Scheduler/options.ts
index aaf69cebb..78ba35378 100644
--- a/frontend/src/pages/Settings/Scheduler/options.ts
+++ b/frontend/src/pages/Settings/Scheduler/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const seriesSyncOptions: SelectorOption<number>[] = [
{ label: "15 Minutes", value: 15 },
{ label: "1 Hour", value: 60 },
diff --git a/frontend/src/pages/Settings/Sonarr/index.tsx b/frontend/src/pages/Settings/Sonarr/index.tsx
index 6dd97b0b6..5baf10b9b 100644
--- a/frontend/src/pages/Settings/Sonarr/index.tsx
+++ b/frontend/src/pages/Settings/Sonarr/index.tsx
@@ -1,4 +1,4 @@
-import React, { FunctionComponent, useCallback } from "react";
+import { FunctionComponent, useCallback } from "react";
import { InputGroup } from "react-bootstrap";
import {
Check,
@@ -6,10 +6,10 @@ import {
CollapseBox,
Group,
Input,
+ Layout,
Message,
PathMappingTable,
Selector,
- SettingsProvider,
Slider,
Text,
URLTestButton,
@@ -17,15 +17,13 @@ import {
import { seriesEnabledKey } from "../keys";
import { seriesTypeOptions } from "../options";
-interface Props {}
-
-const SettingsSonarrView: FunctionComponent<Props> = () => {
+const SettingsSonarrView: FunctionComponent = () => {
const baseUrlOverride = useCallback((settings: Settings) => {
return settings.sonarr.base_url?.slice(1) ?? "";
}, []);
return (
- <SettingsProvider title="Sonarr - Bazarr (Settings)">
+ <Layout name="Sonarr">
<CollapseBox>
<CollapseBox.Control>
<Group header="Use Sonarr">
@@ -116,7 +114,7 @@ const SettingsSonarrView: FunctionComponent<Props> = () => {
</Group>
</CollapseBox.Content>
</CollapseBox>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Subtitles/index.tsx b/frontend/src/pages/Settings/Subtitles/index.tsx
index aab391200..d0380130e 100644
--- a/frontend/src/pages/Settings/Subtitles/index.tsx
+++ b/frontend/src/pages/Settings/Subtitles/index.tsx
@@ -1,12 +1,12 @@
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import {
Check,
CollapseBox,
Group,
Input,
+ Layout,
Message,
Selector,
- SettingsProvider,
Slider,
Text,
} from "../components";
@@ -33,7 +33,7 @@ const subzeroColorOverride = (settings: Settings) => {
const SettingsSubtitlesView: FunctionComponent = () => {
return (
- <SettingsProvider title="Subtitles - Bazarr (Settings)">
+ <Layout name="Subtitles">
<Group header="Subtitles Options">
<CollapseBox>
<CollapseBox.Control>
@@ -514,7 +514,7 @@ const SettingsSubtitlesView: FunctionComponent = () => {
</CollapseBox.Content>
</CollapseBox>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/Subtitles/options.ts b/frontend/src/pages/Settings/Subtitles/options.ts
index fe6adaa2a..5549a4128 100644
--- a/frontend/src/pages/Settings/Subtitles/options.ts
+++ b/frontend/src/pages/Settings/Subtitles/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const hiExtensionOptions: SelectorOption<string>[] = [
{
label: ".hi (Hearing-Impaired)",
diff --git a/frontend/src/pages/Settings/UI/index.tsx b/frontend/src/pages/Settings/UI/index.tsx
index 74e30fada..fb5a58d49 100644
--- a/frontend/src/pages/Settings/UI/index.tsx
+++ b/frontend/src/pages/Settings/UI/index.tsx
@@ -1,12 +1,12 @@
-import React, { FunctionComponent } from "react";
-import { uiPageSizeKey, usePageSize } from "utilities/storage";
-import { Group, Input, Selector, SettingsProvider } from "../components";
+import { uiPageSizeKey, usePageSize } from "@/utilities/storage";
+import { FunctionComponent } from "react";
+import { Group, Input, Layout, Selector } from "../components";
import { pageSizeOptions } from "./options";
const SettingsUIView: FunctionComponent = () => {
const [pageSize] = usePageSize();
return (
- <SettingsProvider title="Interface - Bazarr (Settings)">
+ <Layout name="Interface">
<Group header="UI">
<Input name="Page Size">
<Selector
@@ -16,7 +16,7 @@ const SettingsUIView: FunctionComponent = () => {
></Selector>
</Input>
</Group>
- </SettingsProvider>
+ </Layout>
);
};
diff --git a/frontend/src/pages/Settings/UI/options.ts b/frontend/src/pages/Settings/UI/options.ts
index 9dd6f0d46..57c79c588 100644
--- a/frontend/src/pages/Settings/UI/options.ts
+++ b/frontend/src/pages/Settings/UI/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const pageSizeOptions: SelectorOption<number>[] = [
{
label: "25",
diff --git a/frontend/src/pages/Settings/components/provider.tsx b/frontend/src/pages/Settings/components/Layout.tsx
index 3f364f1e2..aeeb758cf 100644
--- a/frontend/src/pages/Settings/components/provider.tsx
+++ b/frontend/src/pages/Settings/components/Layout.tsx
@@ -1,8 +1,11 @@
+import { useSettingsMutation, useSystemSettings } from "@/apis/hooks";
+import { ContentHeader, LoadingIndicator } from "@/components";
+import { LOG } from "@/utilities/console";
+import { useUpdateLocalStorage } from "@/utilities/storage";
import { faSave } from "@fortawesome/free-solid-svg-icons";
-import { useSettingsMutation, useSystemSettings } from "apis/hooks";
-import { ContentHeader, LoadingIndicator } from "components";
import { merge } from "lodash";
-import React, {
+import {
+ createContext,
FunctionComponent,
useCallback,
useEffect,
@@ -11,9 +14,6 @@ import React, {
} from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
-import { Prompt } from "react-router";
-import { log } from "utilities/logger";
-import { useUpdateLocalStorage } from "utilities/storage";
import {
enabledLanguageKey,
languageProfileKey,
@@ -22,9 +22,14 @@ import {
type SettingDispatcher = Record<string, (settings: LooseObject) => void>;
-export const StagedChangesContext = React.createContext<
- SimpleStateType<LooseObject>
->([{}, () => {}]);
+export const StagedChangesContext = createContext<SimpleStateType<LooseObject>>(
+ [
+ {},
+ () => {
+ throw new Error("StagedChangesContext not initialized");
+ },
+ ]
+);
function submitHooks(settings: LooseObject) {
if (languageProfileKey in settings) {
@@ -44,12 +49,12 @@ function submitHooks(settings: LooseObject) {
}
interface Props {
- title: string;
+ name: string;
children: JSX.Element | JSX.Element[];
}
-const SettingsProvider: FunctionComponent<Props> = (props) => {
- const { children, title } = props;
+const Layout: FunctionComponent<Props> = (props) => {
+ const { children, name } = props;
const updateStorage = useUpdateLocalStorage();
@@ -68,7 +73,7 @@ const SettingsProvider: FunctionComponent<Props> = (props) => {
const saveSettings = useCallback(
(settings: LooseObject) => {
submitHooks(settings);
- log("info", "submitting settings", settings);
+ LOG("info", "submitting settings", settings);
mutate(settings);
},
[mutate]
@@ -134,12 +139,13 @@ const SettingsProvider: FunctionComponent<Props> = (props) => {
return (
<Container fluid>
<Helmet>
- <title>{title}</title>
+ <title>{name} - Bazarr (Settings)</title>
</Helmet>
- <Prompt
+ {/* TODO */}
+ {/* <Prompt
when={Object.keys(stagedChange).length > 0}
message="You have unsaved changes, are you sure you want to leave?"
- ></Prompt>
+ ></Prompt> */}
<ContentHeader>
<ContentHeader.Button
icon={faSave}
@@ -159,4 +165,4 @@ const SettingsProvider: FunctionComponent<Props> = (props) => {
);
};
-export default SettingsProvider;
+export default Layout;
diff --git a/frontend/src/pages/Settings/components/collapse.tsx b/frontend/src/pages/Settings/components/collapse.tsx
index 0f9398481..888c8432e 100644
--- a/frontend/src/pages/Settings/components/collapse.tsx
+++ b/frontend/src/pages/Settings/components/collapse.tsx
@@ -1,4 +1,5 @@
-import React, {
+import {
+ createContext,
Dispatch,
FunctionComponent,
useContext,
@@ -9,11 +10,12 @@ import { Collapse } from "react-bootstrap";
type SupportType = string | boolean;
-const CollapseContext = React.createContext<
- [SupportType, Dispatch<SupportType>]
->(["", (s) => {}]);
-const CollapseUpdateContext = React.createContext<Dispatch<SupportType>>(
- (s) => {}
+const CollapseContext = createContext<
+ [SupportType, Dispatch<SupportType> | undefined]
+>([false, undefined]);
+
+const CollapseUpdateContext = createContext<Dispatch<SupportType> | undefined>(
+ undefined
);
export function useCollapse() {
diff --git a/frontend/src/pages/Settings/components/container.tsx b/frontend/src/pages/Settings/components/container.tsx
index 4808f12c0..bee95240e 100644
--- a/frontend/src/pages/Settings/components/container.tsx
+++ b/frontend/src/pages/Settings/components/container.tsx
@@ -1,8 +1,7 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Card as BSCard, Col, Form, Row } from "react-bootstrap";
-import "./style.scss";
interface GroupProps {
header: string;
diff --git a/frontend/src/pages/Settings/components/forms.tsx b/frontend/src/pages/Settings/components/forms.tsx
index 32ac352e3..d2d089804 100644
--- a/frontend/src/pages/Settings/components/forms.tsx
+++ b/frontend/src/pages/Settings/components/forms.tsx
@@ -5,17 +5,18 @@ import {
FileBrowserProps,
Selector as CSelector,
SelectorProps as CSelectorProps,
+ SelectorValueType,
Slider as CSlider,
SliderProps as CSliderProps,
-} from "components";
+} from "@/components";
+import { isReactText } from "@/utilities";
import { isArray, isBoolean, isNull, isNumber, isString } from "lodash";
-import React, { FunctionComponent, useEffect } from "react";
+import { FunctionComponent, ReactText, useEffect } from "react";
import {
Button as BSButton,
ButtonProps as BSButtonProps,
Form,
} from "react-bootstrap";
-import { isReactText } from "utilities";
import { useCollapse, useLatest } from ".";
import { OverrideFuncType, useSingleUpdate } from "./hooks";
@@ -32,11 +33,11 @@ export interface BaseInput<T> {
disabled?: boolean;
settingKey: string;
override?: OverrideFuncType<T>;
- beforeStaged?: (v: T) => any;
+ beforeStaged?: (v: T) => unknown;
}
-export interface TextProps extends BaseInput<React.ReactText> {
- placeholder?: React.ReactText;
+export interface TextProps extends BaseInput<ReactText> {
+ placeholder?: ReactText;
password?: boolean;
controlled?: boolean;
numberWithArrows?: boolean;
@@ -52,7 +53,7 @@ export const Text: FunctionComponent<TextProps> = ({
settingKey,
numberWithArrows,
}) => {
- const value = useLatest<React.ReactText>(settingKey, isReactText, override);
+ const value = useLatest<ReactText>(settingKey, isReactText, override);
const update = useSingleUpdate();
const collapse = useCollapse();
@@ -76,7 +77,7 @@ export const Text: FunctionComponent<TextProps> = ({
value={controlled ? value ?? undefined : undefined}
onChange={(e) => {
const val = e.currentTarget.value;
- collapse(val.toString());
+ collapse && collapse(val.toString());
const value = beforeStaged ? beforeStaged(val) : val;
update(value, settingKey);
}}
@@ -101,7 +102,7 @@ export const Check: FunctionComponent<CheckProps> = ({
const value = useLatest<boolean>(settingKey, isBoolean, override);
- useEffect(() => collapse(value ?? false), [collapse, value]);
+ useEffect(() => collapse && collapse(value ?? false), [collapse, value]);
return (
<Form.Check
@@ -120,7 +121,7 @@ export const Check: FunctionComponent<CheckProps> = ({
);
};
-function selectorValidator<T>(v: any): v is T {
+function selectorValidator<T>(v: unknown): v is T {
return isString(v) || isNumber(v) || isArray(v);
}
@@ -144,7 +145,7 @@ export function Selector<
useEffect(() => {
if (isString(value) || isNull(value)) {
- collapse(value ?? "");
+ collapse && collapse(value ?? "");
}
});
@@ -153,14 +154,14 @@ export function Selector<
{...selector}
value={value as SelectorValueType<T, M>}
onChange={(v) => {
- v = beforeStaged ? beforeStaged(v) : v;
- update(v, settingKey);
+ const result = beforeStaged ? beforeStaged(v) : v;
+ update(result, settingKey);
}}
></CSelector>
);
}
-type SliderProps = {} & BaseInput<number> &
+type SliderProps = BaseInput<number> &
Omit<CSliderProps, "onChange" | "onAfterChange">;
export const Slider: FunctionComponent<SliderProps> = (props) => {
@@ -181,7 +182,7 @@ export const Slider: FunctionComponent<SliderProps> = (props) => {
);
};
-type ChipsProp = {} & BaseInput<string[]> &
+type ChipsProp = BaseInput<string[]> &
Omit<CChipsProps, "onChange" | "defaultValue">;
export const Chips: FunctionComponent<ChipsProp> = (props) => {
@@ -204,7 +205,7 @@ export const Chips: FunctionComponent<ChipsProp> = (props) => {
type ButtonProps = {
onClick?: (
- update: (v: any, key: string) => void,
+ update: (v: unknown, key: string) => void,
key: string,
value?: string
) => void;
@@ -228,7 +229,7 @@ export const Button: FunctionComponent<Override<ButtonProps, BSButtonProps>> = (
);
};
-type FileProps = {} & BaseInput<string>;
+interface FileProps extends BaseInput<string> {}
export const File: FunctionComponent<Override<FileProps, FileBrowserProps>> = (
props
diff --git a/frontend/src/pages/Settings/components/hooks.ts b/frontend/src/pages/Settings/components/hooks.ts
index 5f1441641..ef4919d9a 100644
--- a/frontend/src/pages/Settings/components/hooks.ts
+++ b/frontend/src/pages/Settings/components/hooks.ts
@@ -1,8 +1,8 @@
-import { useSystemSettings } from "apis/hooks";
+import { useSystemSettings } from "@/apis/hooks";
+import { LOG } from "@/utilities/console";
import { isArray, uniqBy } from "lodash";
import { useCallback, useContext, useMemo } from "react";
-import { log } from "utilities/logger";
-import { StagedChangesContext } from "./provider";
+import { StagedChangesContext } from "./Layout";
export function useStagedValues(): LooseObject {
const [values] = useContext(StagedChangesContext);
@@ -12,12 +12,12 @@ export function useStagedValues(): LooseObject {
export function useSingleUpdate() {
const [, update] = useContext(StagedChangesContext);
return useCallback(
- (v: any, key: string) => {
+ (v: unknown, key: string) => {
update((staged) => {
const changes = { ...staged };
changes[key] = v;
- log("info", "stage settings", changes);
+ LOG("info", "stage settings", changes);
return changes;
});
@@ -33,7 +33,7 @@ export function useMultiUpdate() {
update((staged) => {
const changes = { ...staged, ...obj };
- log("info", "stage settings", changes);
+ LOG("info", "stage settings", changes);
return changes;
});
@@ -42,7 +42,7 @@ export function useMultiUpdate() {
);
}
-type ValidateFuncType<T> = (v: any) => v is T;
+type ValidateFuncType<T> = (v: unknown) => v is T;
export type OverrideFuncType<T> = (settings: Settings) => T;
diff --git a/frontend/src/pages/Settings/components/index.tsx b/frontend/src/pages/Settings/components/index.tsx
index fbec4221f..f420031ed 100644
--- a/frontend/src/pages/Settings/components/index.tsx
+++ b/frontend/src/pages/Settings/components/index.tsx
@@ -1,6 +1,6 @@
-import api from "apis/raw";
+import api from "@/apis/raw";
import { isBoolean, isNumber, isString } from "lodash";
-import React, { FunctionComponent, useCallback, useState } from "react";
+import { FunctionComponent, useCallback, useState } from "react";
import { Button } from "react-bootstrap";
import { useLatest } from "./hooks";
@@ -67,6 +67,6 @@ export { default as CollapseBox } from "./collapse";
export * from "./container";
export * from "./forms";
export * from "./hooks";
+export * from "./Layout";
+export { default as Layout } from "./Layout";
export * from "./pathMapper";
-export * from "./provider";
-export { default as SettingsProvider } from "./provider";
diff --git a/frontend/src/pages/Settings/components/pathMapper.tsx b/frontend/src/pages/Settings/components/pathMapper.tsx
index 19564e7ce..56d07e74e 100644
--- a/frontend/src/pages/Settings/components/pathMapper.tsx
+++ b/frontend/src/pages/Settings/components/pathMapper.tsx
@@ -1,10 +1,17 @@
+import { ActionButton, FileBrowser, SimpleTable } from "@/components";
+import { LOG } from "@/utilities/console";
import { faArrowCircleRight, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ActionButton, FileBrowser, SimpleTable } from "components";
import { capitalize, isArray, isBoolean } from "lodash";
-import React, { FunctionComponent, useCallback, useMemo } from "react";
+import {
+ createContext,
+ FunctionComponent,
+ useCallback,
+ useContext,
+ useMemo,
+} from "react";
import { Button } from "react-bootstrap";
-import { Column, TableUpdater } from "react-table";
+import { Column } from "react-table";
import {
moviesEnabledKey,
pathMappingsKey,
@@ -37,6 +44,16 @@ interface PathMappingItem {
to: string;
}
+type ModifyFn = (index: number, item?: PathMappingItem) => void;
+
+const RowContext = createContext<ModifyFn>(() => {
+ LOG("error", "RowContext not initialized");
+});
+
+function useRowMutation() {
+ return useContext(RowContext);
+}
+
interface TableProps {
type: SupportType;
}
@@ -72,13 +89,13 @@ export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
[items]
);
- const updateCell = useCallback<TableUpdater<PathMappingItem>>(
- (row, item?: PathMappingItem) => {
+ const updateCell = useCallback<ModifyFn>(
+ (index, item) => {
const newItems = [...data];
if (item) {
- newItems[row.index] = item;
+ newItems[index] = item;
} else {
- newItems.splice(row.index, 1);
+ newItems.splice(index, 1);
}
updateRow(newItems);
},
@@ -90,18 +107,22 @@ export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
{
Header: capitalize(type),
accessor: "from",
- Cell: ({ value, row, update }) => (
- <FileBrowser
- drop="up"
- type={type}
- defaultValue={value}
- onChange={(path) => {
- const newItem = { ...row.original };
- newItem.from = path;
- update && update(row, newItem);
- }}
- ></FileBrowser>
- ),
+ Cell: ({ value, row }) => {
+ const mutate = useRowMutation();
+
+ return (
+ <FileBrowser
+ drop="up"
+ type={type}
+ defaultValue={value}
+ onChange={(path) => {
+ const newItem = { ...row.original };
+ newItem.from = path;
+ mutate(row.index, newItem);
+ }}
+ ></FileBrowser>
+ );
+ },
},
{
id: "arrow",
@@ -113,30 +134,35 @@ export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
{
Header: "Bazarr",
accessor: "to",
- Cell: ({ value, row, update }) => (
- <FileBrowser
- drop="up"
- defaultValue={value}
- type="bazarr"
- onChange={(path) => {
- const newItem = { ...row.original };
- newItem.to = path;
- update && update(row, newItem);
- }}
- ></FileBrowser>
- ),
+ Cell: ({ value, row }) => {
+ const mutate = useRowMutation();
+ return (
+ <FileBrowser
+ drop="up"
+ defaultValue={value}
+ type="bazarr"
+ onChange={(path) => {
+ const newItem = { ...row.original };
+ newItem.to = path;
+ mutate(row.index, newItem);
+ }}
+ ></FileBrowser>
+ );
+ },
},
{
id: "action",
accessor: "to",
- Cell: ({ row, update }) => (
- <ActionButton
- icon={faTrash}
- onClick={() => {
- update && update(row);
- }}
- ></ActionButton>
- ),
+ Cell: ({ row }) => {
+ const mutate = useRowMutation();
+
+ return (
+ <ActionButton
+ icon={faTrash}
+ onClick={() => mutate(row.index)}
+ ></ActionButton>
+ );
+ },
},
],
[type]
@@ -144,18 +170,17 @@ export const PathMappingTable: FunctionComponent<TableProps> = ({ type }) => {
if (enabled) {
return (
- <React.Fragment>
+ <RowContext.Provider value={updateCell}>
<SimpleTable
emptyText="No Mapping"
responsive={false}
columns={columns}
data={data}
- update={updateCell}
></SimpleTable>
<Button block variant="light" onClick={addRow}>
Add
</Button>
- </React.Fragment>
+ </RowContext.Provider>
);
} else {
return (
diff --git a/frontend/src/pages/Settings/components/style.scss b/frontend/src/pages/Settings/components/style.scss
deleted file mode 100644
index 83b21eb4e..000000000
--- a/frontend/src/pages/Settings/components/style.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-.settings-card {
- cursor: pointer;
-
- min-height: 85px;
-
- transition: {
- duration: 0.2s;
- timing-function: ease-in-out;
- }
-
- &:hover {
- border-color: var(--primary);
- }
-}
diff --git a/frontend/src/pages/Settings/options.ts b/frontend/src/pages/Settings/options.ts
index 58ff5f490..8f29fb136 100644
--- a/frontend/src/pages/Settings/options.ts
+++ b/frontend/src/pages/Settings/options.ts
@@ -1,3 +1,5 @@
+import { SelectorOption } from "@/components";
+
export const seriesTypeOptions: SelectorOption<string>[] = [
{ label: "Standard", value: "standard" },
{ label: "Anime", value: "anime" },
diff --git a/frontend/src/pages/System/Backups/BackupDeleteModal.tsx b/frontend/src/pages/System/Backups/BackupDeleteModal.tsx
index 906b2f5e1..1f6ea6ece 100644
--- a/frontend/src/pages/System/Backups/BackupDeleteModal.tsx
+++ b/frontend/src/pages/System/Backups/BackupDeleteModal.tsx
@@ -1,10 +1,5 @@
-import {
- AsyncButton,
- BaseModal,
- BaseModalProps,
- useCloseModal,
- useModalPayload,
-} from "components";
+import { AsyncButton, BaseModal, BaseModalProps } from "@/components";
+import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
import React, { FunctionComponent } from "react";
import { Button } from "react-bootstrap";
import { useDeleteBackups } from "../../../apis/hooks";
@@ -12,11 +7,11 @@ import { useDeleteBackups } from "../../../apis/hooks";
interface Props extends BaseModalProps {}
const SystemBackupDeleteModal: FunctionComponent<Props> = ({ ...modal }) => {
- const result = useModalPayload<string>(modal.modalKey);
-
const { mutateAsync } = useDeleteBackups();
- const closeModal = useCloseModal();
+ const result = usePayload<string>(modal.modalKey);
+
+ const { hide } = useModalControl();
const footer = (
<div className="d-flex flex-row-reverse flex-grow-1 justify-content-between">
@@ -25,7 +20,7 @@ const SystemBackupDeleteModal: FunctionComponent<Props> = ({ ...modal }) => {
variant="outline-secondary"
className="mr-2"
onClick={() => {
- closeModal(modal.modalKey);
+ hide(modal.modalKey);
}}
>
Cancel
@@ -39,7 +34,7 @@ const SystemBackupDeleteModal: FunctionComponent<Props> = ({ ...modal }) => {
return null;
}
}}
- onSuccess={() => closeModal(modal.modalKey)}
+ onSuccess={() => hide(modal.modalKey)}
>
Delete
</AsyncButton>
diff --git a/frontend/src/pages/System/Backups/BackupRestoreModal.tsx b/frontend/src/pages/System/Backups/BackupRestoreModal.tsx
index 966d6409c..69d6ae12d 100644
--- a/frontend/src/pages/System/Backups/BackupRestoreModal.tsx
+++ b/frontend/src/pages/System/Backups/BackupRestoreModal.tsx
@@ -1,22 +1,17 @@
-import {
- AsyncButton,
- BaseModal,
- BaseModalProps,
- useCloseModal,
- useModalPayload,
-} from "components";
+import { useRestoreBackups } from "@/apis/hooks/system";
+import { AsyncButton, BaseModal, BaseModalProps } from "@/components";
+import { useModalControl, usePayload } from "@/modules/redux/hooks/modal";
import React, { FunctionComponent } from "react";
import { Button } from "react-bootstrap";
-import { useRestoreBackups } from "../../../apis/hooks";
interface Props extends BaseModalProps {}
const SystemBackupRestoreModal: FunctionComponent<Props> = ({ ...modal }) => {
- const result = useModalPayload<string>(modal.modalKey);
+ const result = usePayload<string>(modal.modalKey);
const { mutateAsync } = useRestoreBackups();
- const closeModal = useCloseModal();
+ const { hide } = useModalControl();
const footer = (
<div className="d-flex flex-row-reverse flex-grow-1 justify-content-between">
@@ -25,7 +20,7 @@ const SystemBackupRestoreModal: FunctionComponent<Props> = ({ ...modal }) => {
variant="outline-secondary"
className="mr-2"
onClick={() => {
- closeModal(modal.modalKey);
+ hide(modal.modalKey);
}}
>
Cancel
@@ -39,7 +34,7 @@ const SystemBackupRestoreModal: FunctionComponent<Props> = ({ ...modal }) => {
return null;
}
}}
- onSuccess={() => closeModal(modal.modalKey)}
+ onSuccess={() => hide(modal.modalKey)}
>
Restore
</AsyncButton>
diff --git a/frontend/src/pages/System/Backups/index.tsx b/frontend/src/pages/System/Backups/index.tsx
index 64225f1c3..fbeb91293 100644
--- a/frontend/src/pages/System/Backups/index.tsx
+++ b/frontend/src/pages/System/Backups/index.tsx
@@ -1,14 +1,12 @@
+import { useCreateBackups, useSystemBackups } from "@/apis/hooks";
+import { ContentHeader, QueryOverlay } from "@/components";
import { faFileArchive } from "@fortawesome/free-solid-svg-icons";
-import { useCreateBackups, useSystemBackups } from "apis/hooks";
-import { ContentHeader, QueryOverlay } from "components";
import React, { FunctionComponent } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import Table from "./table";
-interface Props {}
-
-const SystemBackupsView: FunctionComponent<Props> = () => {
+const SystemBackupsView: FunctionComponent = () => {
const backups = useSystemBackups();
const { mutate: backup, isLoading: isResetting } = useCreateBackups();
diff --git a/frontend/src/pages/System/Backups/table.tsx b/frontend/src/pages/System/Backups/table.tsx
index 7aec9ba2d..4beeb5e7f 100644
--- a/frontend/src/pages/System/Backups/table.tsx
+++ b/frontend/src/pages/System/Backups/table.tsx
@@ -1,6 +1,7 @@
+import { ActionButton, PageTable } from "@/components";
+import { useModalControl } from "@/modules/redux/hooks/modal";
import { faClock, faHistory, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ActionButton, PageTable, useShowModal } from "components";
import React, { FunctionComponent, useMemo } from "react";
import { ButtonGroup } from "react-bootstrap";
import { Column } from "react-table";
@@ -12,7 +13,6 @@ interface Props {
}
const Table: FunctionComponent<Props> = ({ backups }) => {
- const backupModal = useShowModal();
const columns: Column<System.Backups>[] = useMemo<Column<System.Backups>[]>(
() => [
{
@@ -37,24 +37,23 @@ const Table: FunctionComponent<Props> = ({ backups }) => {
{
accessor: "id",
Cell: (row) => {
+ const { show } = useModalControl();
return (
<ButtonGroup>
<ActionButton
icon={faHistory}
- onClick={() =>
- backupModal("restore", row.row.original.filename)
- }
+ onClick={() => show("restore", row.row.original.filename)}
></ActionButton>
<ActionButton
icon={faTrash}
- onClick={() => backupModal("delete", row.row.original.filename)}
+ onClick={() => show("delete", row.row.original.filename)}
></ActionButton>
</ButtonGroup>
);
},
},
],
- [backupModal]
+ []
);
return (
diff --git a/frontend/src/pages/System/Logs/index.tsx b/frontend/src/pages/System/Logs/index.tsx
index 2f835b44b..86cc96df1 100644
--- a/frontend/src/pages/System/Logs/index.tsx
+++ b/frontend/src/pages/System/Logs/index.tsx
@@ -1,15 +1,13 @@
+import { useDeleteLogs, useSystemLogs } from "@/apis/hooks";
+import { ContentHeader, QueryOverlay } from "@/components";
+import { Environment } from "@/utilities";
import { faDownload, faSync, faTrash } from "@fortawesome/free-solid-svg-icons";
-import { useDeleteLogs, useSystemLogs } from "apis/hooks";
-import { ContentHeader, QueryOverlay } from "components";
-import React, { FunctionComponent, useCallback } from "react";
+import { FunctionComponent, useCallback } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
-import { Environment } from "utilities";
import Table from "./table";
-interface Props {}
-
-const SystemLogsView: FunctionComponent<Props> = () => {
+const SystemLogsView: FunctionComponent = () => {
const logs = useSystemLogs();
const { isFetching, data, refetch } = logs;
diff --git a/frontend/src/pages/System/Logs/modal.tsx b/frontend/src/pages/System/Logs/modal.tsx
index c06241cad..946ddf51d 100644
--- a/frontend/src/pages/System/Logs/modal.tsx
+++ b/frontend/src/pages/System/Logs/modal.tsx
@@ -1,10 +1,9 @@
-import { BaseModal, BaseModalProps, useModalPayload } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { BaseModal, BaseModalProps } from "@/components";
+import { usePayload } from "@/modules/redux/hooks/modal";
+import { FunctionComponent, useMemo } from "react";
-interface Props extends BaseModalProps {}
-
-const SystemLogModal: FunctionComponent<Props> = ({ ...modal }) => {
- const stack = useModalPayload<string>(modal.modalKey);
+const SystemLogModal: FunctionComponent<BaseModalProps> = ({ ...modal }) => {
+ const stack = usePayload<string>(modal.modalKey);
const result = useMemo(
() =>
stack?.split("\\n").map((v, idx) => (
@@ -17,7 +16,7 @@ const SystemLogModal: FunctionComponent<Props> = ({ ...modal }) => {
return (
<BaseModal title="Stack traceback" {...modal}>
<pre>
- <code className="zmdi-language-python-alt">{result}</code>
+ <code>{result}</code>
</pre>
</BaseModal>
);
diff --git a/frontend/src/pages/System/Logs/table.tsx b/frontend/src/pages/System/Logs/table.tsx
index b8eace161..04a965ed1 100644
--- a/frontend/src/pages/System/Logs/table.tsx
+++ b/frontend/src/pages/System/Logs/table.tsx
@@ -1,3 +1,5 @@
+import { ActionButton, PageTable } from "@/components";
+import { useModalControl } from "@/modules/redux/hooks/modal";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import {
faBug,
@@ -8,10 +10,9 @@ import {
faQuestion,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ActionButton, PageTable, useShowModal } from "components";
import { isUndefined } from "lodash";
-import React, { FunctionComponent, useCallback, useMemo } from "react";
-import { Column, Row } from "react-table";
+import { FunctionComponent, useMemo } from "react";
+import { Column } from "react-table";
import SystemLogModal from "./modal";
interface Props {
@@ -34,12 +35,6 @@ function mapTypeToIcon(type: System.LogType): IconDefinition {
}
const Table: FunctionComponent<Props> = ({ logs }) => {
- const showModal = useShowModal();
- const show = useCallback(
- (row: Row<System.Log>, text: string) =>
- showModal<string>("system-log", text),
- [showModal]
- );
const columns: Column<System.Log>[] = useMemo<Column<System.Log>[]>(
() => [
{
@@ -59,12 +54,13 @@ const Table: FunctionComponent<Props> = ({ logs }) => {
},
{
accessor: "exception",
- Cell: ({ row, value, update }) => {
+ Cell: ({ value }) => {
+ const { show } = useModalControl();
if (!isUndefined(value)) {
return (
<ActionButton
icon={faLayerGroup}
- onClick={() => update && update(row, value)}
+ onClick={() => show("system-log", value)}
></ActionButton>
);
} else {
@@ -77,10 +73,10 @@ const Table: FunctionComponent<Props> = ({ logs }) => {
);
return (
- <React.Fragment>
- <PageTable columns={columns} data={logs} update={show}></PageTable>
+ <>
+ <PageTable columns={columns} data={logs}></PageTable>
<SystemLogModal size="xl" modalKey="system-log"></SystemLogModal>
- </React.Fragment>
+ </>
);
};
diff --git a/frontend/src/pages/System/Providers/index.tsx b/frontend/src/pages/System/Providers/index.tsx
index f6b3b14fc..a468c4638 100644
--- a/frontend/src/pages/System/Providers/index.tsx
+++ b/frontend/src/pages/System/Providers/index.tsx
@@ -1,14 +1,12 @@
+import { useResetProvider, useSystemProviders } from "@/apis/hooks";
+import { ContentHeader, QueryOverlay } from "@/components";
import { faSync, faTrash } from "@fortawesome/free-solid-svg-icons";
-import { useResetProvider, useSystemProviders } from "apis/hooks";
-import { ContentHeader, QueryOverlay } from "components";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import Table from "./table";
-interface Props {}
-
-const SystemProvidersView: FunctionComponent<Props> = () => {
+const SystemProvidersView: FunctionComponent = () => {
const providers = useSystemProviders();
const { isFetching, data, refetch } = providers;
diff --git a/frontend/src/pages/System/Providers/table.tsx b/frontend/src/pages/System/Providers/table.tsx
index bf2168a5b..47991b3c5 100644
--- a/frontend/src/pages/System/Providers/table.tsx
+++ b/frontend/src/pages/System/Providers/table.tsx
@@ -1,5 +1,5 @@
-import { SimpleTable } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { SimpleTable } from "@/components";
+import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
interface Props {
diff --git a/frontend/src/pages/System/Releases/index.tsx b/frontend/src/pages/System/Releases/index.tsx
index 8863aed6a..32c8bd3e0 100644
--- a/frontend/src/pages/System/Releases/index.tsx
+++ b/frontend/src/pages/System/Releases/index.tsx
@@ -1,13 +1,11 @@
-import { useSystemReleases } from "apis/hooks";
-import { QueryOverlay } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { useSystemReleases } from "@/apis/hooks";
+import { QueryOverlay } from "@/components";
+import { BuildKey } from "@/utilities";
+import { FunctionComponent, useMemo } from "react";
import { Badge, Card, Col, Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
-import { BuildKey } from "utilities";
-interface Props {}
-
-const SystemReleasesView: FunctionComponent<Props> = () => {
+const SystemReleasesView: FunctionComponent = () => {
const releases = useSystemReleases();
const { data } = releases;
@@ -18,13 +16,13 @@ const SystemReleasesView: FunctionComponent<Props> = () => {
</Helmet>
<Row>
<QueryOverlay result={releases}>
- <React.Fragment>
+ <>
{data?.map((v, idx) => (
<Col xs={12} key={BuildKey(idx, v.date)}>
<InfoElement {...v}></InfoElement>
</Col>
))}
- </React.Fragment>
+ </>
</QueryOverlay>
</Row>
</Container>
diff --git a/frontend/src/pages/System/Status/index.tsx b/frontend/src/pages/System/Status/index.tsx
index 5fdd9c446..fc7aaa98c 100644
--- a/frontend/src/pages/System/Status/index.tsx
+++ b/frontend/src/pages/System/Status/index.tsx
@@ -1,3 +1,6 @@
+import { useSystemHealth, useSystemStatus } from "@/apis/hooks";
+import { QueryOverlay } from "@/components";
+import { GithubRepoRoot } from "@/utilities/constants";
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
import {
faDiscord,
@@ -6,20 +9,16 @@ import {
} from "@fortawesome/free-brands-svg-icons";
import { faPaperPlane } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useSystemHealth, useSystemStatus } from "apis/hooks";
-import { QueryOverlay } from "components";
import moment from "moment";
-import React, { FunctionComponent, useState } from "react";
+import { FunctionComponent, ReactNode, useState } from "react";
import { Col, Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import { useIntervalWhen } from "rooks";
-import { GithubRepoRoot } from "utilities/constants";
-import "./style.scss";
import Table from "./table";
interface InfoProps {
title: string;
- children: React.ReactNode;
+ children: ReactNode;
}
function CRow(props: InfoProps): JSX.Element {
@@ -43,12 +42,12 @@ interface IconProps {
function Label(props: IconProps): JSX.Element {
const { icon, link, children } = props;
return (
- <React.Fragment>
+ <>
<FontAwesomeIcon icon={icon} style={{ width: "2rem" }}></FontAwesomeIcon>
<a href={link} target="_blank" rel="noopener noreferrer">
{children}
</a>
- </React.Fragment>
+ </>
);
}
@@ -65,9 +64,7 @@ const InfoContainer: FunctionComponent<{ title: string }> = ({
);
};
-interface Props {}
-
-const SystemStatusView: FunctionComponent<Props> = () => {
+const SystemStatusView: FunctionComponent = () => {
const health = useSystemHealth();
const { data: status } = useSystemStatus();
@@ -77,7 +74,7 @@ const SystemStatusView: FunctionComponent<Props> = () => {
useIntervalWhen(
() => {
if (status) {
- let duration = moment.duration(
+ const duration = moment.duration(
moment().utc().unix() - status.start_time,
"seconds"
),
diff --git a/frontend/src/pages/System/Status/style.scss b/frontend/src/pages/System/Status/style.scss
deleted file mode 100644
index b3f9bbe50..000000000
--- a/frontend/src/pages/System/Status/style.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.status-issue {
- min-width: 16rem;
-}
diff --git a/frontend/src/pages/System/Status/table.tsx b/frontend/src/pages/System/Status/table.tsx
index 3e499f5f4..18b3588f3 100644
--- a/frontend/src/pages/System/Status/table.tsx
+++ b/frontend/src/pages/System/Status/table.tsx
@@ -1,5 +1,5 @@
-import { SimpleTable } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { SimpleTable } from "@/components";
+import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
interface Props {
diff --git a/frontend/src/pages/System/Tasks/index.tsx b/frontend/src/pages/System/Tasks/index.tsx
index 1bb3430f4..f639ae5c6 100644
--- a/frontend/src/pages/System/Tasks/index.tsx
+++ b/frontend/src/pages/System/Tasks/index.tsx
@@ -1,14 +1,12 @@
+import { useSystemTasks } from "@/apis/hooks";
+import { ContentHeader, QueryOverlay } from "@/components";
import { faSync } from "@fortawesome/free-solid-svg-icons";
-import { useSystemTasks } from "apis/hooks";
-import { ContentHeader, QueryOverlay } from "components";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Container, Row } from "react-bootstrap";
import { Helmet } from "react-helmet";
import Table from "./table";
-interface Props {}
-
-const SystemTasksView: FunctionComponent<Props> = () => {
+const SystemTasksView: FunctionComponent = () => {
const tasks = useSystemTasks();
const { isFetching, data, refetch } = tasks;
diff --git a/frontend/src/pages/System/Tasks/table.tsx b/frontend/src/pages/System/Tasks/table.tsx
index e7a9bc42e..d41217efe 100644
--- a/frontend/src/pages/System/Tasks/table.tsx
+++ b/frontend/src/pages/System/Tasks/table.tsx
@@ -1,8 +1,8 @@
+import { useRunTask } from "@/apis/hooks";
+import { AsyncButton, SimpleTable } from "@/components";
import { faSync } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useRunTask } from "apis/hooks";
-import { AsyncButton, SimpleTable } from "components";
-import React, { FunctionComponent, useMemo } from "react";
+import { FunctionComponent, useMemo } from "react";
import { Column, useSortBy } from "react-table";
interface Props {
diff --git a/frontend/src/pages/UIError.tsx b/frontend/src/pages/UIError.tsx
index ba87bb588..4d86dc71b 100644
--- a/frontend/src/pages/UIError.tsx
+++ b/frontend/src/pages/UIError.tsx
@@ -1,9 +1,9 @@
+import { Reload } from "@/utilities";
+import { GithubRepoRoot } from "@/utilities/constants";
import { faDizzy } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import React, { FunctionComponent } from "react";
+import { FunctionComponent } from "react";
import { Button, Container } from "react-bootstrap";
-import { Reload } from "utilities";
-import { GithubRepoRoot } from "utilities/constants";
interface Props {
error: Error;
diff --git a/frontend/src/pages/Wanted/Movies/index.tsx b/frontend/src/pages/Wanted/Movies/index.tsx
index 1acf379c6..1be04cee0 100644
--- a/frontend/src/pages/Wanted/Movies/index.tsx
+++ b/frontend/src/pages/Wanted/Movies/index.tsx
@@ -1,21 +1,20 @@
-import { faSearch } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useMovieAction,
useMovieSubtitleModification,
useMovieWantedPagination,
-} from "apis/hooks";
-import { AsyncButton, LanguageText } from "components";
-import WantedView from "components/views/WantedView";
-import React, { FunctionComponent, useMemo } from "react";
+} from "@/apis/hooks";
+import { AsyncButton } from "@/components";
+import Language from "@/components/bazarr/Language";
+import WantedView from "@/components/views/WantedView";
+import { BuildKey } from "@/utilities";
+import { faSearch } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FunctionComponent, useMemo } from "react";
import { Badge } from "react-bootstrap";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-import { BuildKey } from "utilities";
-
-interface Props {}
-const WantedMoviesView: FunctionComponent<Props> = () => {
+const WantedMoviesView: FunctionComponent = () => {
const columns: Column<Wanted.Movie>[] = useMemo<Column<Wanted.Movie>[]>(
() => [
{
@@ -56,7 +55,7 @@ const WantedMoviesView: FunctionComponent<Props> = () => {
})
}
>
- <LanguageText className="pr-1" text={item}></LanguageText>
+ <Language.Text className="pr-1" value={item}></Language.Text>
<FontAwesomeIcon size="sm" icon={faSearch}></FontAwesomeIcon>
</AsyncButton>
));
diff --git a/frontend/src/pages/Wanted/Series/index.tsx b/frontend/src/pages/Wanted/Series/index.tsx
index f4548dc48..e9d6ccd70 100644
--- a/frontend/src/pages/Wanted/Series/index.tsx
+++ b/frontend/src/pages/Wanted/Series/index.tsx
@@ -1,21 +1,20 @@
-import { faSearch } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useEpisodeSubtitleModification,
useEpisodeWantedPagination,
useSeriesAction,
-} from "apis/hooks";
-import { AsyncButton, LanguageText } from "components";
-import WantedView from "components/views/WantedView";
-import React, { FunctionComponent, useMemo } from "react";
+} from "@/apis/hooks";
+import { AsyncButton } from "@/components";
+import Language from "@/components/bazarr/Language";
+import WantedView from "@/components/views/WantedView";
+import { BuildKey } from "@/utilities";
+import { faSearch } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FunctionComponent, useMemo } from "react";
import { Badge } from "react-bootstrap";
import { Link } from "react-router-dom";
import { Column } from "react-table";
-import { BuildKey } from "utilities";
-
-interface Props {}
-const WantedSeriesView: FunctionComponent<Props> = () => {
+const WantedSeriesView: FunctionComponent = () => {
const columns: Column<Wanted.Episode>[] = useMemo<Column<Wanted.Episode>[]>(
() => [
{
@@ -66,7 +65,7 @@ const WantedSeriesView: FunctionComponent<Props> = () => {
})
}
>
- <LanguageText className="pr-1" text={item}></LanguageText>
+ <Language.Text className="pr-1" value={item}></Language.Text>
<FontAwesomeIcon size="sm" icon={faSearch}></FontAwesomeIcon>
</AsyncButton>
));