Add Deluge integration; revamp admin Jobs & Logs UI

Introduce Deluge download client service and tests, remove obsolete rdtclient service, and update qbittorrent integration/tests and download-client interfaces/manager. Large UI refactor for admin pages: Jobs and Logs were redesigned to be responsive (mobile card views + desktop tables), improved headers, dialogs, controls, and better status/detail rendering. Also updated DownloadClient components (card, management, modal), organize-files processor, audible-series integration, and related unit tests to align with integration changes. Minor UX and accessibility tweaks, cron handling/validation adjustments, and a few formatting/cleanup fixes throughout.
This commit is contained in:
kikootwo
2026-02-20 20:44:26 -05:00
parent 04dbb05a6e
commit d70f6c9957
22 changed files with 1742 additions and 679 deletions
+3 -3
View File
@@ -55,9 +55,9 @@ describe('AdminJobsPage', () => {
render(<AdminJobsPage />);
expect(await screen.findByText('Library Scan')).toBeInTheDocument();
expect((await screen.findAllByText('Library Scan'))[0]).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /Trigger Now/i }));
fireEvent.click(screen.getAllByRole('button', { name: /Trigger Now/i })[0]);
fireEvent.click(screen.getByRole('button', { name: 'Trigger Job' }));
await waitFor(() => {
@@ -88,7 +88,7 @@ describe('AdminJobsPage', () => {
render(<AdminJobsPage />);
fireEvent.click(await screen.findByRole('button', { name: 'Edit' }));
fireEvent.click((await screen.findAllByRole('button', { name: 'Edit' }))[0]);
fireEvent.click(screen.getByRole('radio', { name: /Every 2 hours/i }));
fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));
+7 -7
View File
@@ -68,14 +68,14 @@ describe('AdminLogsPage', () => {
render(<AdminLogsPage />);
expect(await screen.findByText('System Logs')).toBeInTheDocument();
expect(screen.getByText('Search Book')).toBeInTheDocument();
expect(screen.getAllByText('Search Book')[0]).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Show Details' }));
expect(screen.getByText('Event Log')).toBeInTheDocument();
expect(screen.getByText('Job Result')).toBeInTheDocument();
expect(screen.getByText('Error')).toBeInTheDocument();
fireEvent.click(screen.getAllByRole('button', { name: 'Show Details' })[0]);
expect(screen.getAllByText('Event Log')[0]).toBeInTheDocument();
expect(screen.getAllByText('Job Result')[0]).toBeInTheDocument();
expect(screen.getAllByText('Error')[0]).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Hide Details' }));
fireEvent.click(screen.getAllByRole('button', { name: 'Hide Details' })[0]);
expect(screen.queryByText('Event Log')).not.toBeInTheDocument();
});
@@ -122,6 +122,6 @@ describe('AdminLogsPage', () => {
render(<AdminLogsPage />);
expect(await screen.findByText('No logs found')).toBeInTheDocument();
expect((await screen.findAllByText('No logs found'))[0]).toBeInTheDocument();
});
});
+9 -9
View File
@@ -158,9 +158,9 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
expect(await screen.findByText('Full Access')).toBeDefined();
expect(screen.getByText('Manual')).toBeDefined();
expect(screen.getByText('Auto-Approve')).toBeDefined();
expect((await screen.findAllByText('Full Access'))[0]).toBeDefined();
expect(screen.getAllByText('Manual')[0]).toBeDefined();
expect(screen.getAllByText('Auto-Approve')[0]).toBeDefined();
});
it('shows Global Default badge when global auto-approve is on', async () => {
@@ -171,7 +171,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
expect(await screen.findByText('Global Default')).toBeDefined();
expect((await screen.findAllByText('Global Default'))[0]).toBeDefined();
});
it('opens user permissions modal and shows admin lock state for both permissions', async () => {
@@ -184,7 +184,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
// Click the permissions badge to open modal
fireEvent.click(await screen.findByText('Full Access'));
fireEvent.click((await screen.findAllByText('Full Access'))[0]);
// Modal should show user info and the locked state for both permissions
expect(await screen.findByText('User Permissions')).toBeDefined();
@@ -205,7 +205,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
// Click the Manual badge to open permissions modal
fireEvent.click(await screen.findByText('Manual'));
fireEvent.click((await screen.findAllByText('Manual'))[0]);
// Find and click the auto-approve toggle switch inside the modal
const toggle = await screen.findByRole('switch', { name: 'Auto-Approve Requests' });
@@ -231,7 +231,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
// Click the Manual badge to open permissions modal
fireEvent.click(await screen.findByText('Manual'));
fireEvent.click((await screen.findAllByText('Manual'))[0]);
// Find and click the interactive search toggle switch inside the modal
const toggle = await screen.findByRole('switch', { name: 'Interactive Search Access' });
@@ -255,7 +255,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
// Click the Global Default badge
fireEvent.click(await screen.findByText('Global Default'));
fireEvent.click((await screen.findAllByText('Global Default'))[0]);
// Modal should show the global override message for both
expect(await screen.findByText('Controlled by global auto-approve setting')).toBeDefined();
@@ -288,7 +288,7 @@ describe('AdminUsersPage', () => {
render(<AdminUsersPage />);
fireEvent.click(await screen.findByRole('button', { name: 'Edit Role' }));
fireEvent.click((await screen.findAllByRole('button', { name: 'Edit Role' }))[0]);
fireEvent.click(screen.getByRole('radio', { name: /Admin/i }));
fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));