From 4386999c380f6fd0457824541dd855f148b30e51 Mon Sep 17 00:00:00 2001
From: Ben Phelps <ben@phelps.io>
Date: Mon, 26 Sep 2022 15:25:10 +0300
Subject: [PATCH] further restructuring

---
 src/components/bookmarks/item.jsx              |  2 +-
 src/components/services/item.jsx               |  4 ++--
 .../services/{widgets => widget}/block.jsx     |  0
 .../widget.jsx => widget/container.jsx}        |  2 +-
 .../{color-toggle.jsx => toggles/color.jsx}    |  0
 src/components/{ => toggles}/revalidate.jsx    |  0
 .../{theme-toggle.jsx => toggles/theme.jsx}    |  0
 src/components/version.jsx                     |  4 ++--
 src/components/{ => widgets}/widget.jsx        |  0
 src/pages/api/docker/stats/[...service].js     |  2 +-
 src/pages/api/docker/status/[...service].js    |  2 +-
 src/pages/api/validate.js                      |  2 +-
 src/pages/api/widgets/openweathermap.js        |  2 +-
 src/pages/api/widgets/weather.js               |  2 +-
 src/pages/index.jsx                            | 10 +++++-----
 src/utils/config/api-response.js               |  4 ++--
 src/utils/{ => config}/config.js               |  0
 src/utils/{ => config}/docker.js               |  2 +-
 src/utils/{ => config}/service-helpers.js      |  4 ++--
 src/utils/proxy/handlers/credentialed.js       |  2 +-
 src/utils/proxy/handlers/generic.js            |  2 +-
 src/widgets/adguard/component.jsx              | 14 +++++++-------
 src/widgets/authentik/component.jsx            | 14 +++++++-------
 src/widgets/bazarr/component.jsx               | 14 +++++++-------
 src/widgets/coinmarketcap/component.jsx        | 18 +++++++++---------
 src/widgets/docker/component.jsx               | 18 +++++++++---------
 src/widgets/emby/component.jsx                 |  4 ++--
 src/widgets/gotify/component.jsx               | 10 +++++-----
 src/widgets/jackett/component.jsx              | 14 +++++++-------
 src/widgets/jellyseerr/component.jsx           | 14 +++++++-------
 src/widgets/lidarr/component.jsx               | 14 +++++++-------
 src/widgets/mastodon/component.jsx             | 14 +++++++-------
 src/widgets/npm/component.jsx                  | 14 +++++++-------
 src/widgets/npm/proxy.js                       |  2 +-
 src/widgets/nzbget/component.jsx               | 14 +++++++-------
 src/widgets/nzbget/proxy.js                    |  2 +-
 src/widgets/ombi/component.jsx                 | 14 +++++++-------
 src/widgets/overseerr/component.jsx            | 14 +++++++-------
 src/widgets/pihole/component.jsx               | 14 +++++++-------
 src/widgets/portainer/component.jsx            | 16 ++++++++--------
 src/widgets/prowlarr/component.jsx             | 14 +++++++-------
 src/widgets/qbittorrent/component.jsx          | 14 +++++++-------
 src/widgets/qbittorrent/proxy.js               |  2 +-
 src/widgets/radarr/component.jsx               | 14 +++++++-------
 src/widgets/readarr/component.jsx              | 14 +++++++-------
 src/widgets/rutorrent/component.jsx            | 14 +++++++-------
 src/widgets/rutorrent/proxy.js                 |  2 +-
 src/widgets/sabnzbd/component.jsx              | 14 +++++++-------
 src/widgets/sonarr/component.jsx               | 14 +++++++-------
 src/widgets/speedtest/component.jsx            | 14 +++++++-------
 src/widgets/strelaysrv/component.jsx           | 14 +++++++-------
 src/widgets/tautulli/component.jsx             |  4 ++--
 src/widgets/traefik/component.jsx              | 14 +++++++-------
 src/widgets/transmission/component.jsx         | 14 +++++++-------
 src/widgets/transmission/proxy.js              |  2 +-
 55 files changed, 224 insertions(+), 224 deletions(-)
 rename src/components/services/{widgets => widget}/block.jsx (100%)
 rename src/components/services/{widgets/widget.jsx => widget/container.jsx} (82%)
 rename src/components/{color-toggle.jsx => toggles/color.jsx} (100%)
 rename src/components/{ => toggles}/revalidate.jsx (100%)
 rename src/components/{theme-toggle.jsx => toggles/theme.jsx} (100%)
 rename src/components/{ => widgets}/widget.jsx (100%)
 rename src/utils/{ => config}/config.js (100%)
 rename src/utils/{ => config}/docker.js (93%)
 rename src/utils/{ => config}/service-helpers.js (97%)

diff --git a/src/components/bookmarks/item.jsx b/src/components/bookmarks/item.jsx
index f3a2be53..71b0cf08 100644
--- a/src/components/bookmarks/item.jsx
+++ b/src/components/bookmarks/item.jsx
@@ -20,7 +20,7 @@ export default function Item({ bookmark }) {
           </div>
           <div className="flex-1 flex items-center justify-between rounded-r-md ">
             <div className="flex-1 grow pl-3 py-2 text-xs">{bookmark.name}</div>
-            <div className="px-2 py-2 truncate text-theme-500 dark:text-theme-400 text-xs">{hostname}</div>
+            <div className="px-2 py-2 truncate text-theme-500 dark:text-theme-300 text-xs">{hostname}</div>
           </div>
         </div>
       </a>
diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx
index de70914e..6f945515 100644
--- a/src/components/services/item.jsx
+++ b/src/components/services/item.jsx
@@ -74,14 +74,14 @@ export default function Item({ service }) {
             >
               <div className="flex-1 px-2 py-2 text-sm text-left">
                 {service.name}
-                <p className="text-theme-500 dark:text-theme-400 text-xs font-light">{service.description}</p>
+                <p className="text-theme-500 dark:text-theme-300 text-xs font-light">{service.description}</p>
               </div>
             </a>
           ) : (
             <div className="flex-1 flex items-center justify-between rounded-r-md ">
               <div className="flex-1 px-2 py-2 text-sm text-left">
                 {service.name}
-                <p className="text-theme-500 dark:text-theme-400 text-xs font-light">{service.description}</p>
+                <p className="text-theme-500 dark:text-theme-300 text-xs font-light">{service.description}</p>
               </div>
             </div>
           )}
diff --git a/src/components/services/widgets/block.jsx b/src/components/services/widget/block.jsx
similarity index 100%
rename from src/components/services/widgets/block.jsx
rename to src/components/services/widget/block.jsx
diff --git a/src/components/services/widgets/widget.jsx b/src/components/services/widget/container.jsx
similarity index 82%
rename from src/components/services/widgets/widget.jsx
rename to src/components/services/widget/container.jsx
index 12202c05..deb5fdc2 100644
--- a/src/components/services/widgets/widget.jsx
+++ b/src/components/services/widget/container.jsx
@@ -1,4 +1,4 @@
-export default function Widget({ error = false, children }) {
+export default function Container({ error = false, children }) {
   if (error) {
     return (
       <div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1">
diff --git a/src/components/color-toggle.jsx b/src/components/toggles/color.jsx
similarity index 100%
rename from src/components/color-toggle.jsx
rename to src/components/toggles/color.jsx
diff --git a/src/components/revalidate.jsx b/src/components/toggles/revalidate.jsx
similarity index 100%
rename from src/components/revalidate.jsx
rename to src/components/toggles/revalidate.jsx
diff --git a/src/components/theme-toggle.jsx b/src/components/toggles/theme.jsx
similarity index 100%
rename from src/components/theme-toggle.jsx
rename to src/components/toggles/theme.jsx
diff --git a/src/components/version.jsx b/src/components/version.jsx
index 18b7b7e9..72449a7a 100644
--- a/src/components/version.jsx
+++ b/src/components/version.jsx
@@ -26,7 +26,7 @@ export default function Version() {
 
   return (
     <div className="flex flex-row items-center">
-      <span className="text-xs text-theme-500">
+      <span className="text-xs text-theme-500 dark:text-theme-400">
         {version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
       </span>
       {version === "main" || version === "dev" || version === "nightly"
@@ -37,7 +37,7 @@ export default function Version() {
               href={latestRelease.html_url}
               target="_blank"
               rel="noopener noreferrer"
-              className="ml-2 text-xs text-theme-500 flex flex-row items-center"
+              className="ml-2 text-xs text-theme-500 dark:text-theme-400 flex flex-row items-center"
             >
               <MdNewReleases className="mr-1" /> {t("Update Available")}
             </a>
diff --git a/src/components/widget.jsx b/src/components/widgets/widget.jsx
similarity index 100%
rename from src/components/widget.jsx
rename to src/components/widgets/widget.jsx
diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js
index f8f4e5f0..ca8c8bd3 100644
--- a/src/pages/api/docker/stats/[...service].js
+++ b/src/pages/api/docker/stats/[...service].js
@@ -1,6 +1,6 @@
 import Docker from "dockerode";
 
-import getDockerArguments from "utils/docker";
+import getDockerArguments from "utils/config/docker";
 
 export default async function handler(req, res) {
   const { service } = req.query;
diff --git a/src/pages/api/docker/status/[...service].js b/src/pages/api/docker/status/[...service].js
index 8a14041d..8330ac3d 100644
--- a/src/pages/api/docker/status/[...service].js
+++ b/src/pages/api/docker/status/[...service].js
@@ -1,6 +1,6 @@
 import Docker from "dockerode";
 
-import getDockerArguments from "utils/docker";
+import getDockerArguments from "utils/config/docker";
 
 export default async function handler(req, res) {
   const { service } = req.query;
diff --git a/src/pages/api/validate.js b/src/pages/api/validate.js
index cdb28274..bab53057 100644
--- a/src/pages/api/validate.js
+++ b/src/pages/api/validate.js
@@ -1,4 +1,4 @@
-import checkAndCopyConfig from "utils/config";
+import checkAndCopyConfig from "utils/config/config";
 
 const configs = ["docker.yaml", "settings.yaml", "services.yaml", "bookmarks.yaml"];
 
diff --git a/src/pages/api/widgets/openweathermap.js b/src/pages/api/widgets/openweathermap.js
index b94c9d8d..a2070997 100644
--- a/src/pages/api/widgets/openweathermap.js
+++ b/src/pages/api/widgets/openweathermap.js
@@ -1,5 +1,5 @@
 import cachedFetch from "utils/proxy/cached-fetch";
-import { getSettings } from "utils/config";
+import { getSettings } from "utils/config/config";
 
 export default async function handler(req, res) {
   const { latitude, longitude, units, provider, cache, lang } = req.query;
diff --git a/src/pages/api/widgets/weather.js b/src/pages/api/widgets/weather.js
index 893d3054..5cba47ba 100644
--- a/src/pages/api/widgets/weather.js
+++ b/src/pages/api/widgets/weather.js
@@ -1,5 +1,5 @@
 import cachedFetch from "utils/proxy/cached-fetch";
-import { getSettings } from "utils/config";
+import { getSettings } from "utils/config/config";
 
 export default async function handler(req, res) {
   const { latitude, longitude, provider, cache, lang } = req.query;
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index a1faafb7..25179e80 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -9,20 +9,20 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
 
 import ServicesGroup from "components/services/group";
 import BookmarksGroup from "components/bookmarks/group";
-import Widget from "components/widget";
-import Revalidate from "components/revalidate";
+import Widget from "components/widgets/widget";
+import Revalidate from "components/toggles/revalidate";
 import createLogger from "utils/logger";
-import { getSettings } from "utils/config";
+import { getSettings } from "utils/config/config";
 import { ColorContext } from "utils/contexts/color";
 import { ThemeContext } from "utils/contexts/theme";
 import { SettingsContext } from "utils/contexts/settings";
 import { bookmarksResponse, servicesResponse, widgetsResponse } from "utils/config/api-response";
 
-const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
+const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
   ssr: false,
 });
 
-const ColorToggle = dynamic(() => import("components/color-toggle"), {
+const ColorToggle = dynamic(() => import("components/toggles/color"), {
   ssr: false,
 });
 
diff --git a/src/utils/config/api-response.js b/src/utils/config/api-response.js
index 502b2796..0b929b13 100644
--- a/src/utils/config/api-response.js
+++ b/src/utils/config/api-response.js
@@ -4,8 +4,8 @@ import path from "path";
 
 import yaml from "js-yaml";
 
-import checkAndCopyConfig from "utils/config";
-import { servicesFromConfig, servicesFromDocker, cleanServiceGroups } from "utils/service-helpers";
+import checkAndCopyConfig from "utils/config/config";
+import { servicesFromConfig, servicesFromDocker, cleanServiceGroups } from "utils/config/service-helpers";
 
 export async function bookmarksResponse() {
   checkAndCopyConfig("bookmarks.yaml");
diff --git a/src/utils/config.js b/src/utils/config/config.js
similarity index 100%
rename from src/utils/config.js
rename to src/utils/config/config.js
diff --git a/src/utils/docker.js b/src/utils/config/docker.js
similarity index 93%
rename from src/utils/docker.js
rename to src/utils/config/docker.js
index 14fb5c8d..9aef7408 100644
--- a/src/utils/docker.js
+++ b/src/utils/config/docker.js
@@ -3,7 +3,7 @@ import { readFileSync } from "fs";
 
 import yaml from "js-yaml";
 
-import checkAndCopyConfig from "utils/config";
+import checkAndCopyConfig from "utils/config/config";
 
 export default function getDockerArguments(server) {
   checkAndCopyConfig("docker.yaml");
diff --git a/src/utils/service-helpers.js b/src/utils/config/service-helpers.js
similarity index 97%
rename from src/utils/service-helpers.js
rename to src/utils/config/service-helpers.js
index 8918e264..f36e0a56 100644
--- a/src/utils/service-helpers.js
+++ b/src/utils/config/service-helpers.js
@@ -5,8 +5,8 @@ import yaml from "js-yaml";
 import Docker from "dockerode";
 import * as shvl from "shvl";
 
-import checkAndCopyConfig from "utils/config";
-import getDockerArguments from "utils/docker";
+import checkAndCopyConfig from "utils/config/config";
+import getDockerArguments from "utils/config/docker";
 
 export async function servicesFromConfig() {
   checkAndCopyConfig("services.yaml");
diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js
index 7bea34f7..d14ef0e1 100644
--- a/src/utils/proxy/handlers/credentialed.js
+++ b/src/utils/proxy/handlers/credentialed.js
@@ -1,4 +1,4 @@
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 import { formatApiCall } from "utils/proxy/api-helpers";
 import { httpProxy } from "utils/proxy/http";
 import createLogger from "utils/logger";
diff --git a/src/utils/proxy/handlers/generic.js b/src/utils/proxy/handlers/generic.js
index b25042f1..f93c83f2 100644
--- a/src/utils/proxy/handlers/generic.js
+++ b/src/utils/proxy/handlers/generic.js
@@ -1,4 +1,4 @@
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 import { formatApiCall } from "utils/proxy/api-helpers";
 import { httpProxy } from "utils/proxy/http";
 import createLogger from "utils/logger";
diff --git a/src/widgets/adguard/component.jsx b/src/widgets/adguard/component.jsx
index e6f0d6be..d16fc40c 100644
--- a/src/widgets/adguard/component.jsx
+++ b/src/widgets/adguard/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,17 +13,17 @@ export default function Component({ service }) {
   const { data: adguardData, error: adguardError } = useSWR(formatProxyUrl(config, "stats"));
 
   if (adguardError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!adguardData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("adguard.queries")} />
         <Block label={t("adguard.blocked")} />
         <Block label={t("adguard.filtered")} />
         <Block label={t("adguard.latency")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -31,7 +31,7 @@ export default function Component({ service }) {
     adguardData.num_replaced_safebrowsing + adguardData.num_replaced_safesearch + adguardData.num_replaced_parental;
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("adguard.queries")} value={t("common.number", { value: adguardData.num_dns_queries })} />
       <Block label={t("adguard.blocked")} value={t("common.number", { value: adguardData.num_blocked_filtering })} />
       <Block label={t("adguard.filtered")} value={t("common.number", { value: filtered })} />
@@ -39,6 +39,6 @@ export default function Component({ service }) {
         label={t("adguard.latency")}
         value={t("common.ms", { value: adguardData.avg_processing_time * 1000, style: "unit", unit: "millisecond" })}
       />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/authentik/component.jsx b/src/widgets/authentik/component.jsx
index 63fef6f3..af7e7600 100644
--- a/src/widgets/authentik/component.jsx
+++ b/src/widgets/authentik/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -15,16 +15,16 @@ export default function Component({ service }) {
   const { data: failedLoginsData, error: failedLoginsError } = useSWR(formatProxyUrl(config, "login_failed"));
 
   if (usersError || loginsError || failedLoginsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!usersData || !loginsData || !failedLoginsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("authentik.users")} />
         <Block label={t("authentik.loginsLast24H")} />
         <Block label={t("authentik.failedLoginsLast24H")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -39,10 +39,10 @@ export default function Component({ service }) {
   );
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("authentik.users")} value={t("common.number", { value: usersData.pagination.count })} />
       <Block label={t("authentik.loginsLast24H")} value={t("common.number", { value: loginsLast24H })} />
       <Block label={t("authentik.failedLoginsLast24H")} value={t("common.number", { value: failedLoginsLast24H })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/bazarr/component.jsx b/src/widgets/bazarr/component.jsx
index fd0630ce..99338bfb 100644
--- a/src/widgets/bazarr/component.jsx
+++ b/src/widgets/bazarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -14,22 +14,22 @@ export default function Component({ service }) {
   const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movies"));
 
   if (episodesError || moviesError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!episodesData || !moviesData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("bazarr.missingEpisodes")} />
         <Block label={t("bazarr.missingMovies")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("bazarr.missingEpisodes")} value={t("common.number", { value: episodesData.total })} />
       <Block label={t("bazarr.missingMovies")} value={t("common.number", { value: moviesData.total })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/coinmarketcap/component.jsx b/src/widgets/coinmarketcap/component.jsx
index 88f70041..abd7efb5 100644
--- a/src/widgets/coinmarketcap/component.jsx
+++ b/src/widgets/coinmarketcap/component.jsx
@@ -3,8 +3,8 @@ import { useState } from "react";
 import { useTranslation } from "next-i18next";
 import classNames from "classnames";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import Dropdown from "components/services/dropdown";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
@@ -33,28 +33,28 @@ export default function Component({ service }) {
 
   if (!symbols || symbols.length === 0) {
     return (
-      <Widget>
+      <Container>
         <Block value={t("coinmarketcap.configure")} />
-      </Widget>
+      </Container>
     );
   }
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData || !dateRange) {
     return (
-      <Widget>
+      <Container>
         <Block value={t("coinmarketcap.configure")} />
-      </Widget>
+      </Container>
     );
   }
 
   const { data } = statsData;
 
   return (
-    <Widget>
+    <Container>
       <div className={classNames(service.description ? "-top-10" : "-top-8", "absolute right-1")}>
         <Dropdown options={dateRangeOptions} value={dateRange} setValue={setDateRange} />
       </div>
@@ -87,6 +87,6 @@ export default function Component({ service }) {
           </div>
         ))}
       </div>
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/docker/component.jsx b/src/widgets/docker/component.jsx
index a9c26b1c..719e7c9c 100644
--- a/src/widgets/docker/component.jsx
+++ b/src/widgets/docker/component.jsx
@@ -3,8 +3,8 @@ import { useTranslation } from "next-i18next";
 
 import calculateCPUPercent from "./stats-helpers";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 
 export default function Component({ service }) {
   const { t } = useTranslation();
@@ -18,30 +18,30 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(`/api/docker/stats/${config.container}/${config.server || ""}`);
 
   if (statsError || statusError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (statusData && statusData.status !== "running") {
     return (
-      <Widget>
+      <Container>
         <Block label={t("widget.status")} value={t("docker.offline")} />
-      </Widget>
+      </Container>
     );
   }
 
   if (!statsData || !statusData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("docker.cpu")} />
         <Block label={t("docker.mem")} />
         <Block label={t("docker.rx")} />
         <Block label={t("docker.tx")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("docker.cpu")} value={t("common.percent", { value: calculateCPUPercent(statsData.stats) })} />
       <Block label={t("docker.mem")} value={t("common.bytes", { value: statsData.stats.memory_stats.usage })} />
       {statsData.stats.networks && (
@@ -50,6 +50,6 @@ export default function Component({ service }) {
           <Block label={t("docker.tx")} value={t("common.bytes", { value: statsData.stats.networks.eth0.tx_bytes })} />
         </>
       )}
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/emby/component.jsx b/src/widgets/emby/component.jsx
index c3217b3a..6602ee93 100644
--- a/src/widgets/emby/component.jsx
+++ b/src/widgets/emby/component.jsx
@@ -3,7 +3,7 @@ import { useTranslation } from "next-i18next";
 import { BsVolumeMuteFill, BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs";
 import { MdOutlineSmartDisplay } from "react-icons/md";
 
-import Widget from "components/services/widgets/widget";
+import Container from "components/services/widget/container";
 import { formatProxyUrl, formatProxyUrlWithSegments } from "utils/proxy/api-helpers";
 
 function ticksToTime(ticks) {
@@ -172,7 +172,7 @@ export default function Component({ service }) {
   }
 
   if (sessionsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!sessionsData) {
diff --git a/src/widgets/gotify/component.jsx b/src/widgets/gotify/component.jsx
index 943a7478..63bcbfcf 100644
--- a/src/widgets/gotify/component.jsx
+++ b/src/widgets/gotify/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -15,14 +15,14 @@ export default function Component({ service }) {
   const { data: clientsData, error: clientsError } = useSWR(formatProxyUrl(config, `client`));
 
   if (appsError || messagesError || clientsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("gotify.apps")} value={appsData?.length} />
       <Block label={t("gotify.clients")} value={clientsData?.length} />
       <Block label={t("gotify.messages")} value={messagesData?.messages?.length} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/jackett/component.jsx b/src/widgets/jackett/component.jsx
index e1d4083f..903028ef 100644
--- a/src/widgets/jackett/component.jsx
+++ b/src/widgets/jackett/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: indexersData, error: indexersError } = useSWR(formatProxyUrl(config, "indexers"));
 
   if (indexersError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!indexersData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("jackett.configured")} />
         <Block label={t("jackett.errored")} />
-      </Widget>
+      </Container>
     );
   }
 
   const errored = indexersData.filter((indexer) => indexer.last_error);
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("jackett.configured")} value={t("common.number", { value: indexersData.length })} />
       <Block label={t("jackett.errored")} value={t("common.number", { value: errored.length })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/jellyseerr/component.jsx b/src/widgets/jellyseerr/component.jsx
index 1254d8d4..a38e2091 100644
--- a/src/widgets/jellyseerr/component.jsx
+++ b/src/widgets/jellyseerr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `request/count`));
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("jellyseerr.pending")} />
         <Block label={t("jellyseerr.approved")} />
         <Block label={t("jellyseerr.available")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("jellyseerr.pending")} value={statsData.pending} />
       <Block label={t("jellyseerr.approved")} value={statsData.approved} />
       <Block label={t("jellyseerr.available")} value={statsData.available} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/lidarr/component.jsx b/src/widgets/lidarr/component.jsx
index 588ca94b..9cd99c99 100644
--- a/src/widgets/lidarr/component.jsx
+++ b/src/widgets/lidarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -15,24 +15,24 @@ export default function Component({ service }) {
   const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue/status"));
 
   if (albumsError || wantedError || queueError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!albumsData || !wantedData || !queueData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("lidarr.wanted")} />
         <Block label={t("lidarr.queued")} />
         <Block label={t("lidarr.albums")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("lidarr.wanted")} value={t("common.number", { value: wantedData.totalRecords })} />
       <Block label={t("lidarr.queued")} value={t("common.number", { value: queueData.totalCount })} />
       <Block label={t("lidarr.albums")} value={t("common.number", { value: albumsData.have })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/mastodon/component.jsx b/src/widgets/mastodon/component.jsx
index cbd3635d..34b57ffe 100644
--- a/src/widgets/mastodon/component.jsx
+++ b/src/widgets/mastodon/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `instance`));
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("mastodon.user_count")} />
         <Block label={t("mastodon.status_count")} />
         <Block label={t("mastodon.domain_count")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("mastodon.user_count")} value={t("common.number", { value: statsData.stats.user_count })} />
       <Block label={t("mastodon.status_count")} value={t("common.number", { value: statsData.stats.status_count })} />
       <Block label={t("mastodon.domain_count")} value={t("common.number", { value: statsData.stats.domain_count })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/npm/component.jsx b/src/widgets/npm/component.jsx
index 0d2ab75e..aea4776f 100644
--- a/src/widgets/npm/component.jsx
+++ b/src/widgets/npm/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,16 +13,16 @@ export default function Component({ service }) {
   const { data: infoData, error: infoError } = useSWR(formatProxyUrl(config, "nginx/proxy-hosts"));
 
   if (infoError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!infoData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("npm.enabled")} />
         <Block label={t("npm.disabled")} />
         <Block label={t("npm.total")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -31,10 +31,10 @@ export default function Component({ service }) {
   const total = infoData.length;
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("npm.enabled")} value={enabled} />
       <Block label={t("npm.disabled")} value={disabled} />
       <Block label={t("npm.total")} value={total} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/npm/proxy.js b/src/widgets/npm/proxy.js
index 44e760eb..eed43b57 100644
--- a/src/widgets/npm/proxy.js
+++ b/src/widgets/npm/proxy.js
@@ -1,4 +1,4 @@
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 import { formatApiCall } from "utils/proxy/api-helpers";
 import widgets from "widgets/widgets";
 
diff --git a/src/widgets/nzbget/component.jsx b/src/widgets/nzbget/component.jsx
index c57ba415..a7a9f3be 100644
--- a/src/widgets/nzbget/component.jsx
+++ b/src/widgets/nzbget/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,21 +13,21 @@ export default function Component({ service }) {
   const { data: statusData, error: statusError } = useSWR(formatProxyUrl(config, "status"));
 
   if (statusError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statusData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("nzbget.rate")} />
         <Block label={t("nzbget.remaining")} />
         <Block label={t("nzbget.downloaded")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("nzbget.rate")} value={t("common.bitrate", { value: statusData.DownloadRate })} />
       <Block
         label={t("nzbget.remaining")}
@@ -37,6 +37,6 @@ export default function Component({ service }) {
         label={t("nzbget.downloaded")}
         value={t("common.bytes", { value: statusData.DownloadedSizeMB * 1024 * 1024 })}
       />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/nzbget/proxy.js b/src/widgets/nzbget/proxy.js
index 7f0a450d..4feac781 100644
--- a/src/widgets/nzbget/proxy.js
+++ b/src/widgets/nzbget/proxy.js
@@ -1,6 +1,6 @@
 import { JSONRPCClient } from "json-rpc-2.0";
 
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 
 export default async function nzbgetProxyHandler(req, res) {
   const { group, service, endpoint } = req.query;
diff --git a/src/widgets/ombi/component.jsx b/src/widgets/ombi/component.jsx
index d2b9544b..779f89eb 100644
--- a/src/widgets/ombi/component.jsx
+++ b/src/widgets/ombi/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `Request/count`));
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("ombi.pending")} />
         <Block label={t("ombi.approved")} />
         <Block label={t("ombi.available")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("ombi.pending")} value={statsData.pending} />
       <Block label={t("ombi.approved")} value={statsData.approved} />
       <Block label={t("ombi.available")} value={statsData.available} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/overseerr/component.jsx b/src/widgets/overseerr/component.jsx
index cb49dfba..49c98bea 100644
--- a/src/widgets/overseerr/component.jsx
+++ b/src/widgets/overseerr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, "request/count"));
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("overseerr.pending")} />
         <Block label={t("overseerr.approved")} />
         <Block label={t("overseerr.available")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("overseerr.pending")} value={statsData.pending} />
       <Block label={t("overseerr.approved")} value={statsData.approved} />
       <Block label={t("overseerr.available")} value={statsData.available} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/pihole/component.jsx b/src/widgets/pihole/component.jsx
index e6c6862d..5b4fdc87 100644
--- a/src/widgets/pihole/component.jsx
+++ b/src/widgets/pihole/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: piholeData, error: piholeError } = useSWR(formatProxyUrl(config, "api.php"));
 
   if (piholeError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!piholeData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("pihole.queries")} />
         <Block label={t("pihole.blocked")} />
         <Block label={t("pihole.gravity")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("pihole.queries")} value={t("common.number", { value: piholeData.dns_queries_today })} />
       <Block label={t("pihole.blocked")} value={t("common.number", { value: piholeData.ads_blocked_today })} />
       <Block label={t("pihole.gravity")} value={t("common.number", { value: piholeData.domains_being_blocked })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/portainer/component.jsx b/src/widgets/portainer/component.jsx
index 171a7f46..e62a6929 100644
--- a/src/widgets/portainer/component.jsx
+++ b/src/widgets/portainer/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -17,21 +17,21 @@ export default function Component({ service }) {
   );
 
   if (containersError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!containersData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("portainer.running")} />
         <Block label={t("portainer.stopped")} />
         <Block label={t("portainer.total")} />
-      </Widget>
+      </Container>
     );
   }
 
   if (containersData.error) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   const running = containersData.filter((c) => c.State === "running").length;
@@ -39,10 +39,10 @@ export default function Component({ service }) {
   const total = containersData.length;
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("portainer.running")} value={running} />
       <Block label={t("portainer.stopped")} value={stopped} />
       <Block label={t("portainer.total")} value={total} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/prowlarr/component.jsx b/src/widgets/prowlarr/component.jsx
index 9a351493..5345a40e 100644
--- a/src/widgets/prowlarr/component.jsx
+++ b/src/widgets/prowlarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -14,18 +14,18 @@ export default function Component({ service }) {
   const { data: grabsData, error: grabsError } = useSWR(formatProxyUrl(config, "indexerstats"));
 
   if (indexersError || grabsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!indexersData || !grabsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("prowlarr.enableIndexers")} />
         <Block label={t("prowlarr.numberOfGrabs")} />
         <Block label={t("prowlarr.numberOfQueries")} />
         <Block label={t("prowlarr.numberOfFailGrabs")} />
         <Block label={t("prowlarr.numberOfFailQueries")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -43,12 +43,12 @@ export default function Component({ service }) {
   });
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("prowlarr.enableIndexers")} value={indexers.length} />
       <Block label={t("prowlarr.numberOfGrabs")} value={numberOfGrabs} />
       <Block label={t("prowlarr.numberOfQueries")} value={numberOfQueries} />
       <Block label={t("prowlarr.numberOfFailGrabs")} value={numberOfFailedGrabs} />
       <Block label={t("prowlarr.numberOfFailQueries")} value={numberOfFailedQueries} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/qbittorrent/component.jsx b/src/widgets/qbittorrent/component.jsx
index d38c7eaf..27c7b2f3 100644
--- a/src/widgets/qbittorrent/component.jsx
+++ b/src/widgets/qbittorrent/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,17 +13,17 @@ export default function Component({ service }) {
   const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config, "torrents/info"));
 
   if (torrentError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!torrentData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("qbittorrent.leech")} />
         <Block label={t("qbittorrent.download")} />
         <Block label={t("qbittorrent.seed")} />
         <Block label={t("qbittorrent.upload")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -58,11 +58,11 @@ export default function Component({ service }) {
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("qbittorrent.leech")} value={t("common.number", { value: leech })} />
       <Block label={t("qbittorrent.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
       <Block label={t("qbittorrent.seed")} value={t("common.number", { value: completed })} />
       <Block label={t("qbittorrent.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/qbittorrent/proxy.js b/src/widgets/qbittorrent/proxy.js
index e9a83e13..a585ea63 100644
--- a/src/widgets/qbittorrent/proxy.js
+++ b/src/widgets/qbittorrent/proxy.js
@@ -1,7 +1,7 @@
 import { formatApiCall } from "utils/proxy/api-helpers";
 import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
 import { httpProxy } from "utils/proxy/http";
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 
 async function login(widget, params) {
   const loginUrl = new URL(`${widget.url}/api/v2/auth/login`);
diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx
index 856724ef..be931312 100644
--- a/src/widgets/radarr/component.jsx
+++ b/src/widgets/radarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -14,24 +14,24 @@ export default function Component({ service }) {
   const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue/status"));
 
   if (moviesError || queuedError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!moviesData || !queuedData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("radarr.wanted")} />
         <Block label={t("radarr.queued")} />
         <Block label={t("radarr.movies")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("radarr.wanted")} value={moviesData.wanted} />
       <Block label={t("radarr.queued")} value={queuedData.totalCount} />
       <Block label={t("radarr.movies")} value={moviesData.have} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/readarr/component.jsx b/src/widgets/readarr/component.jsx
index 64852ded..572ff700 100644
--- a/src/widgets/readarr/component.jsx
+++ b/src/widgets/readarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -15,24 +15,24 @@ export default function Component({ service }) {
   const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue/status"));
 
   if (booksError || wantedError || queueError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!booksData || !wantedData || !queueData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("readarr.wanted")} />
         <Block label={t("readarr.queued")} />
         <Block label={t("readarr.books")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("readarr.wanted")} value={t("common.number", { value: wantedData.totalRecords })} />
       <Block label={t("readarr.queued")} value={t("common.number", { value: queueData.totalCount })} />
       <Block label={t("readarr.books")} value={t("common.number", { value: booksData.have })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/rutorrent/component.jsx b/src/widgets/rutorrent/component.jsx
index 0f3a45bd..146c023d 100644
--- a/src/widgets/rutorrent/component.jsx
+++ b/src/widgets/rutorrent/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,16 +13,16 @@ export default function Component({ service }) {
   const { data: statusData, error: statusError } = useSWR(formatProxyUrl(config));
 
   if (statusError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statusData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("rutorrent.active")} />
         <Block label={t("rutorrent.upload")} />
         <Block label={t("rutorrent.download")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -33,10 +33,10 @@ export default function Component({ service }) {
   const active = statusData.filter((torrent) => torrent["d.get_state"] === "1");
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("rutorrent.active")} value={active.length} />
       <Block label={t("rutorrent.upload")} value={t("common.bitrate", { value: upload })} />
       <Block label={t("rutorrent.download")} value={t("common.bitrate", { value: download })} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/rutorrent/proxy.js b/src/widgets/rutorrent/proxy.js
index 78e7b815..dae50c35 100644
--- a/src/widgets/rutorrent/proxy.js
+++ b/src/widgets/rutorrent/proxy.js
@@ -1,6 +1,6 @@
 import RuTorrent from "rutorrent-promise";
 
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 
 export default async function rutorrentProxyHandler(req, res) {
   const { group, service } = req.query;
diff --git a/src/widgets/sabnzbd/component.jsx b/src/widgets/sabnzbd/component.jsx
index 6ca6e35a..56bd2b3b 100644
--- a/src/widgets/sabnzbd/component.jsx
+++ b/src/widgets/sabnzbd/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue"));
 
   if (queueError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!queueData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("sabnzbd.rate")} />
         <Block label={t("sabnzbd.queue")} />
         <Block label={t("sabnzbd.timeleft")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}B/s`} />
       <Block label={t("sabnzbd.queue")} value={t("common.number", { value: queueData.queue.noofslots })} />
       <Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx
index 05f5ecb4..7e5dbf25 100644
--- a/src/widgets/sonarr/component.jsx
+++ b/src/widgets/sonarr/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -15,24 +15,24 @@ export default function Component({ service }) {
   const { data: seriesData, error: seriesError } = useSWR(formatProxyUrl(config, "series"));
 
   if (wantedError || queuedError || seriesError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!wantedData || !queuedData || !seriesData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("sonarr.wanted")} />
         <Block label={t("sonarr.queued")} />
         <Block label={t("sonarr.series")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("sonarr.wanted")} value={wantedData.totalRecords} />
       <Block label={t("sonarr.queued")} value={queuedData.totalRecords} />
       <Block label={t("sonarr.series")} value={seriesData.total} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/speedtest/component.jsx b/src/widgets/speedtest/component.jsx
index bc15f840..4ae7df2b 100644
--- a/src/widgets/speedtest/component.jsx
+++ b/src/widgets/speedtest/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,21 +13,21 @@ export default function Component({ service }) {
   const { data: speedtestData, error: speedtestError } = useSWR(formatProxyUrl(config, "speedtest/latest"));
 
   if (speedtestError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!speedtestData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("speedtest.download")} />
         <Block label={t("speedtest.upload")} />
         <Block label={t("speedtest.ping")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block
         label={t("speedtest.download")}
         value={t("common.bitrate", { value: speedtestData.data.download * 1024 * 1024 })}
@@ -40,6 +40,6 @@ export default function Component({ service }) {
         label={t("speedtest.ping")}
         value={t("common.ms", { value: speedtestData.data.ping, style: "unit", unit: "millisecond" })}
       />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/strelaysrv/component.jsx b/src/widgets/strelaysrv/component.jsx
index b77be020..9dca1b21 100644
--- a/src/widgets/strelaysrv/component.jsx
+++ b/src/widgets/strelaysrv/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,21 +13,21 @@ export default function Component({ service }) {
   const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `status`));
 
   if (statsError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!statsData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("strelaysrv.numActiveSessions")} />
         <Block label={t("strelaysrv.numConnections")} />
         <Block label={t("strelaysrv.bytesProxied")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block
         label={t("strelaysrv.numActiveSessions")}
         value={t("common.number", { value: statsData.numActiveSessions })}
@@ -38,6 +38,6 @@ export default function Component({ service }) {
         label={t("strelaysrv.transferRate")}
         value={t("common.bitrate", { value: statsData.kbps10s1m5m15m30m60m[5] })}
       />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/tautulli/component.jsx b/src/widgets/tautulli/component.jsx
index a4bb1d81..40627017 100644
--- a/src/widgets/tautulli/component.jsx
+++ b/src/widgets/tautulli/component.jsx
@@ -4,7 +4,7 @@ import { useTranslation } from "next-i18next";
 import { BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs";
 import { MdOutlineSmartDisplay, MdSmartDisplay } from "react-icons/md";
 
-import Widget from "components/services/widgets/widget";
+import Container from "components/services/widget/container";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 function millisecondsToTime(milliseconds) {
@@ -124,7 +124,7 @@ export default function Component({ service }) {
   });
 
   if (activityError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!activityData) {
diff --git a/src/widgets/traefik/component.jsx b/src/widgets/traefik/component.jsx
index 5ffb767f..219db5d5 100644
--- a/src/widgets/traefik/component.jsx
+++ b/src/widgets/traefik/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,24 +13,24 @@ export default function Component({ service }) {
   const { data: traefikData, error: traefikError } = useSWR(formatProxyUrl(config, "overview"));
 
   if (traefikError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!traefikData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("traefik.routers")} />
         <Block label={t("traefik.services")} />
         <Block label={t("traefik.middleware")} />
-      </Widget>
+      </Container>
     );
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("traefik.routers")} value={traefikData.http.routers.total} />
       <Block label={t("traefik.services")} value={traefikData.http.services.total} />
       <Block label={t("traefik.middleware")} value={traefikData.http.middlewares.total} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/transmission/component.jsx b/src/widgets/transmission/component.jsx
index c4e7560a..55b6d884 100644
--- a/src/widgets/transmission/component.jsx
+++ b/src/widgets/transmission/component.jsx
@@ -1,8 +1,8 @@
 import useSWR from "swr";
 import { useTranslation } from "next-i18next";
 
-import Widget from "components/services/widgets/widget";
-import Block from "components/services/widgets/block";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
 import { formatProxyUrl } from "utils/proxy/api-helpers";
 
 export default function Component({ service }) {
@@ -13,17 +13,17 @@ export default function Component({ service }) {
   const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config));
 
   if (torrentError) {
-    return <Widget error={t("widget.api_error")} />;
+    return <Container error={t("widget.api_error")} />;
   }
 
   if (!torrentData) {
     return (
-      <Widget>
+      <Container>
         <Block label={t("transmission.leech")} />
         <Block label={t("transmission.download")} />
         <Block label={t("transmission.seed")} />
         <Block label={t("transmission.upload")} />
-      </Widget>
+      </Container>
     );
   }
 
@@ -59,11 +59,11 @@ export default function Component({ service }) {
   }
 
   return (
-    <Widget>
+    <Container>
       <Block label={t("transmission.leech")} value={t("common.number", { value: leech })} />
       <Block label={t("transmission.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
       <Block label={t("transmission.seed")} value={t("common.number", { value: completed })} />
       <Block label={t("transmission.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
-    </Widget>
+    </Container>
   );
 }
diff --git a/src/widgets/transmission/proxy.js b/src/widgets/transmission/proxy.js
index 5d118922..4ea10cce 100644
--- a/src/widgets/transmission/proxy.js
+++ b/src/widgets/transmission/proxy.js
@@ -1,6 +1,6 @@
 import { httpProxy } from "utils/proxy/http";
 import { formatApiCall } from "utils/proxy/api-helpers";
-import getServiceWidget from "utils/service-helpers";
+import getServiceWidget from "utils/config/service-helpers";
 
 export default async function transmissionProxyHandler(req, res) {
   const { group, service, endpoint } = req.query;