mirror of
https://github.com/gethomepage/homepage.git
synced 2026-09-27 14:31:17 -07:00
Chore: merge Overseerr into Seerr, add aliases (#6330)
This commit is contained in:
@@ -101,7 +101,6 @@ You can also find a list of all available service widgets in the sidebar navigat
|
|||||||
- [OpenMediaVault](openmediavault.md)
|
- [OpenMediaVault](openmediavault.md)
|
||||||
- [OpenWRT](openwrt.md)
|
- [OpenWRT](openwrt.md)
|
||||||
- [OPNsense](opnsense.md)
|
- [OPNsense](opnsense.md)
|
||||||
- [Overseerr](overseerr.md)
|
|
||||||
- [PaperlessNGX](paperlessngx.md)
|
- [PaperlessNGX](paperlessngx.md)
|
||||||
- [Peanut](peanut.md)
|
- [Peanut](peanut.md)
|
||||||
- [pfSense](pfsense.md)
|
- [pfSense](pfsense.md)
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
title: Overseerr
|
|
||||||
description: Overseerr Widget Configuration
|
|
||||||
---
|
|
||||||
|
|
||||||
Learn more about [Overseerr](https://github.com/sct/overseerr).
|
|
||||||
|
|
||||||
Find your API key under `Settings > General`.
|
|
||||||
|
|
||||||
Allowed fields: `["pending", "approved", "available", "processing"]`.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
widget:
|
|
||||||
type: overseerr
|
|
||||||
url: http://overseerr.host.or.ip
|
|
||||||
key: apikeyapikeyapikeyapikeyapikey
|
|
||||||
```
|
|
||||||
@@ -7,9 +7,9 @@ Learn more about [Seerr](https://github.com/seerr-team/seerr).
|
|||||||
|
|
||||||
Find your API key under `Settings > General > API Key`.
|
Find your API key under `Settings > General > API Key`.
|
||||||
|
|
||||||
_Note that Jellyseerr was merged with Overseerr and renamed Seerr. Use `type: seerr` (legacy `type: jellyseerr` is aliased)._
|
_Jellyseerr and Overseerr merged into Seerr. Use `type: seerr` (legacy `type: jellyseerr` and `type: overseerr` are aliased)._
|
||||||
|
|
||||||
Allowed fields: `["pending", "approved", "available", "completed", "issues"]`.
|
Allowed fields: `["pending", "approved", "available", "completed", "processing", "issues"]`.
|
||||||
Default fields: `["pending", "approved", "completed"]`.
|
Default fields: `["pending", "approved", "completed"]`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ nav:
|
|||||||
- widgets/services/openmediavault.md
|
- widgets/services/openmediavault.md
|
||||||
- widgets/services/opnsense.md
|
- widgets/services/opnsense.md
|
||||||
- widgets/services/openwrt.md
|
- widgets/services/openwrt.md
|
||||||
- widgets/services/overseerr.md
|
|
||||||
- widgets/services/pangolin.md
|
- widgets/services/pangolin.md
|
||||||
- widgets/services/paperlessngx.md
|
- widgets/services/paperlessngx.md
|
||||||
- widgets/services/peanut.md
|
- widgets/services/peanut.md
|
||||||
|
|||||||
@@ -294,13 +294,8 @@
|
|||||||
"approved": "Approved",
|
"approved": "Approved",
|
||||||
"available": "Available",
|
"available": "Available",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"issues": "Open Issues"
|
|
||||||
},
|
|
||||||
"overseerr": {
|
|
||||||
"pending": "Pending",
|
|
||||||
"processing": "Processing",
|
"processing": "Processing",
|
||||||
"approved": "Approved",
|
"issues": "Open Issues"
|
||||||
"available": "Available"
|
|
||||||
},
|
},
|
||||||
"netalertx": {
|
"netalertx": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { buildHighlightConfig } from "utils/highlights";
|
|||||||
const ALIASED_WIDGETS = {
|
const ALIASED_WIDGETS = {
|
||||||
pialert: "netalertx",
|
pialert: "netalertx",
|
||||||
hoarder: "karakeep",
|
hoarder: "karakeep",
|
||||||
|
jellyseerr: "seerr",
|
||||||
|
overseerr: "seerr",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Container({ error = false, children, service }) {
|
export default function Container({ error = false, children, service }) {
|
||||||
|
|||||||
@@ -58,6 +58,26 @@ describe("components/services/widget/container", () => {
|
|||||||
expect(screen.getByTestId("karakeep.count")).toBeInTheDocument();
|
expect(screen.getByTestId("karakeep.count")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("supports seerr aliases when filtering (jellyseerr/overseerr -> seerr)", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<Container service={{ widget: { type: "jellyseerr", fields: ["pending"] } }}>
|
||||||
|
<Dummy label="seerr.pending" />
|
||||||
|
</Container>,
|
||||||
|
{ settings: {} },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId("seerr.pending")).toBeInTheDocument();
|
||||||
|
|
||||||
|
renderWithProviders(
|
||||||
|
<Container service={{ widget: { type: "overseerr", fields: ["processing"] } }}>
|
||||||
|
<Dummy label="seerr.processing" />
|
||||||
|
</Container>,
|
||||||
|
{ settings: {} },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByTestId("seerr.processing")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("returns null when errors are hidden via settings.hideErrors", () => {
|
it("returns null when errors are hidden via settings.hideErrors", () => {
|
||||||
const { container } = renderWithProviders(
|
const { container } = renderWithProviders(
|
||||||
<Container error="nope" service={{ widget: { type: "omada", hide_errors: false } }}>
|
<Container error="nope" service={{ widget: { type: "omada", hide_errors: false } }}>
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const components = {
|
|||||||
ombi: dynamic(() => import("./ombi/component")),
|
ombi: dynamic(() => import("./ombi/component")),
|
||||||
opendtu: dynamic(() => import("./opendtu/component")),
|
opendtu: dynamic(() => import("./opendtu/component")),
|
||||||
opnsense: dynamic(() => import("./opnsense/component")),
|
opnsense: dynamic(() => import("./opnsense/component")),
|
||||||
overseerr: dynamic(() => import("./overseerr/component")),
|
overseerr: dynamic(() => import("./seerr/component")),
|
||||||
openmediavault: dynamic(() => import("./openmediavault/component")),
|
openmediavault: dynamic(() => import("./openmediavault/component")),
|
||||||
openwrt: dynamic(() => import("./openwrt/component")),
|
openwrt: dynamic(() => import("./openwrt/component")),
|
||||||
paperlessngx: dynamic(() => import("./paperlessngx/component")),
|
paperlessngx: dynamic(() => import("./paperlessngx/component")),
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ const MAX_ALLOWED_FIELDS = 4;
|
|||||||
|
|
||||||
export default function Component({ service }) {
|
export default function Component({ service }) {
|
||||||
const { widget } = service;
|
const { widget } = service;
|
||||||
|
|
||||||
widget.fields = widget?.fields?.length ? widget.fields.slice(0, MAX_ALLOWED_FIELDS) : seerrDefaultFields;
|
widget.fields = widget?.fields?.length ? widget.fields.slice(0, MAX_ALLOWED_FIELDS) : seerrDefaultFields;
|
||||||
const isIssueEnabled = widget.fields.includes("issues");
|
const isIssueEnabled = widget.fields.includes("issues");
|
||||||
|
|
||||||
@@ -21,11 +20,12 @@ export default function Component({ service }) {
|
|||||||
if (!statsData || (isIssueEnabled && !issueData)) {
|
if (!statsData || (isIssueEnabled && !issueData)) {
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="seerr.pending" />
|
<Block field="seerr.pending" label="seerr.pending" />
|
||||||
<Block label="seerr.approved" />
|
<Block field="seerr.approved" label="seerr.approved" />
|
||||||
<Block label="seerr.available" />
|
<Block field="seerr.available" label="seerr.available" />
|
||||||
<Block label="seerr.completed" />
|
<Block field="seerr.completed" label="seerr.completed" />
|
||||||
<Block label="seerr.issues" />
|
<Block field="seerr.processing" label="seerr.processing" />
|
||||||
|
<Block field="seerr.issues" label="seerr.issues" />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -38,11 +38,12 @@ export default function Component({ service }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="seerr.pending" value={statsData.pending} />
|
<Block field="seerr.pending" label="seerr.pending" value={statsData.pending} />
|
||||||
<Block label="seerr.approved" value={statsData.approved} />
|
<Block field="seerr.approved" label="seerr.approved" value={statsData.approved} />
|
||||||
<Block label="seerr.available" value={statsData.available} />
|
<Block field="seerr.available" label="seerr.available" value={statsData.available} />
|
||||||
<Block label="seerr.completed" value={statsData.completed} />
|
<Block field="seerr.completed" label="seerr.completed" value={statsData.completed} />
|
||||||
<Block label="seerr.issues" value={`${issueData?.open} / ${issueData?.total}`} />
|
<Block field="seerr.processing" label="seerr.processing" value={statsData.processing} />
|
||||||
|
<Block field="seerr.issues" label="seerr.issues" value={`${issueData?.open} / ${issueData?.total}`} />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,59 @@ describe("widgets/seerr/component", () => {
|
|||||||
expect(screen.getByText("seerr.approved")).toBeInTheDocument();
|
expect(screen.getByText("seerr.approved")).toBeInTheDocument();
|
||||||
expect(screen.getByText("seerr.completed")).toBeInTheDocument();
|
expect(screen.getByText("seerr.completed")).toBeInTheDocument();
|
||||||
expect(screen.queryByText("seerr.available")).toBeNull();
|
expect(screen.queryByText("seerr.available")).toBeNull();
|
||||||
|
expect(screen.queryByText("seerr.processing")).toBeNull();
|
||||||
expect(screen.queryByText("seerr.issues")).toBeNull();
|
expect(screen.queryByText("seerr.issues")).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("supports jellyseerr as a legacy alias to seerr", () => {
|
||||||
|
useWidgetAPI
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined }) // request/count
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined }); // issue/count disabled (endpoint = "")
|
||||||
|
|
||||||
|
const service = { widget: { type: "jellyseerr", url: "http://x" } };
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(service.widget.fields).toEqual(seerrDefaultFields);
|
||||||
|
expect(useWidgetAPI.mock.calls[1][1]).toBe("");
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||||
|
expect(screen.getByText("seerr.pending")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("seerr.approved")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("seerr.completed")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports overseerr as a legacy alias with the same default fields", () => {
|
||||||
|
useWidgetAPI
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined }) // request/count
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined }); // issue/count disabled (endpoint = "")
|
||||||
|
|
||||||
|
const service = { widget: { type: "overseerr", url: "http://x" } };
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(service.widget.fields).toEqual(seerrDefaultFields);
|
||||||
|
expect(useWidgetAPI.mock.calls[1][1]).toBe("");
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||||
|
expect(screen.getByText("seerr.pending")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("seerr.approved")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("seerr.completed")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps processing as a separate optional field", () => {
|
||||||
|
useWidgetAPI
|
||||||
|
.mockReturnValueOnce({ data: { pending: 1, processing: 2, approved: 3, available: 4 }, error: undefined })
|
||||||
|
.mockReturnValueOnce({ data: undefined, error: undefined }); // issue/count disabled (endpoint = "")
|
||||||
|
|
||||||
|
const service = {
|
||||||
|
widget: { type: "overseerr", url: "http://x", fields: ["pending", "processing", "approved", "available"] },
|
||||||
|
};
|
||||||
|
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
|
||||||
|
|
||||||
|
expect(useWidgetAPI.mock.calls[1][1]).toBe("");
|
||||||
|
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
|
||||||
|
expect(screen.getByText("seerr.processing")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("2")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("seerr.completed")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("renders issues when enabled (and calls the issue/count endpoint)", () => {
|
it("renders issues when enabled (and calls the issue/count endpoint)", () => {
|
||||||
useWidgetAPI
|
useWidgetAPI
|
||||||
.mockReturnValueOnce({ data: { pending: 1, approved: 2, available: 3, completed: 4 }, error: undefined })
|
.mockReturnValueOnce({ data: { pending: 1, approved: 2, available: 3, completed: 4 }, error: undefined })
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ import opendtu from "./opendtu/widget";
|
|||||||
import openmediavault from "./openmediavault/widget";
|
import openmediavault from "./openmediavault/widget";
|
||||||
import openwrt from "./openwrt/widget";
|
import openwrt from "./openwrt/widget";
|
||||||
import opnsense from "./opnsense/widget";
|
import opnsense from "./opnsense/widget";
|
||||||
import overseerr from "./overseerr/widget";
|
|
||||||
import pangolin from "./pangolin/widget";
|
import pangolin from "./pangolin/widget";
|
||||||
import paperlessngx from "./paperlessngx/widget";
|
import paperlessngx from "./paperlessngx/widget";
|
||||||
import peanut from "./peanut/widget";
|
import peanut from "./peanut/widget";
|
||||||
@@ -244,7 +243,7 @@ const widgets = {
|
|||||||
ombi,
|
ombi,
|
||||||
opendtu,
|
opendtu,
|
||||||
opnsense,
|
opnsense,
|
||||||
overseerr,
|
overseerr: seerr,
|
||||||
openmediavault,
|
openmediavault,
|
||||||
openwrt,
|
openwrt,
|
||||||
paperlessngx,
|
paperlessngx,
|
||||||
|
|||||||
Reference in New Issue
Block a user