mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: Improved Indexer disabled popover
This commit is contained in:
11
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.css
Normal file
11
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.title {
|
||||||
|
composes: title from '~Components/DescriptionList/DescriptionListItemTitle.css';
|
||||||
|
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
composes: title from '~Components/DescriptionList/DescriptionListItemDescription.css';
|
||||||
|
|
||||||
|
margin-left: 110px;
|
||||||
|
}
|
8
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.css.d.ts
vendored
Normal file
8
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.css.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'description': string;
|
||||||
|
'title': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
51
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.tsx
Normal file
51
frontend/src/Indexer/Index/Table/DisabledIndexerInfo.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||||
|
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||||
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './DisabledIndexerInfo.css';
|
||||||
|
|
||||||
|
interface DisabledIndexerInfoProps {
|
||||||
|
mostRecentFailure: Date;
|
||||||
|
disabledTill: Date;
|
||||||
|
initialFailure: Date;
|
||||||
|
longDateFormat: string;
|
||||||
|
timeFormat: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DisabledIndexerInfo(props: DisabledIndexerInfoProps) {
|
||||||
|
const {
|
||||||
|
mostRecentFailure,
|
||||||
|
disabledTill,
|
||||||
|
initialFailure,
|
||||||
|
longDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DescriptionList>
|
||||||
|
<DescriptionListItem
|
||||||
|
titleClassName={styles.title}
|
||||||
|
descriptionClassName={styles.description}
|
||||||
|
title={translate('InitialFailure')}
|
||||||
|
data={formatDateTime(initialFailure, longDateFormat, timeFormat)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DescriptionListItem
|
||||||
|
titleClassName={styles.title}
|
||||||
|
descriptionClassName={styles.description}
|
||||||
|
title={translate('LastFailure')}
|
||||||
|
data={formatDateTime(mostRecentFailure, longDateFormat, timeFormat)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DescriptionListItem
|
||||||
|
titleClassName={styles.title}
|
||||||
|
descriptionClassName={styles.description}
|
||||||
|
title={translate('DisabledUntil')}
|
||||||
|
data={formatDateTime(disabledTill, longDateFormat, timeFormat)}
|
||||||
|
/>
|
||||||
|
</DescriptionList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DisabledIndexerInfo;
|
@@ -30,9 +30,8 @@ interface IndexerIndexRowProps {
|
|||||||
function IndexerIndexRow(props: IndexerIndexRowProps) {
|
function IndexerIndexRow(props: IndexerIndexRowProps) {
|
||||||
const { indexerId, columns, isSelectMode } = props;
|
const { indexerId, columns, isSelectMode } = props;
|
||||||
|
|
||||||
const { indexer, appProfile } = useSelector(
|
const { indexer, appProfile, status, longDateFormat, timeFormat } =
|
||||||
createIndexerIndexItemSelector(props.indexerId)
|
useSelector(createIndexerIndexItemSelector(props.indexerId));
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
@@ -44,7 +43,6 @@ function IndexerIndexRow(props: IndexerIndexRowProps) {
|
|||||||
protocol,
|
protocol,
|
||||||
privacy,
|
privacy,
|
||||||
priority,
|
priority,
|
||||||
status,
|
|
||||||
fields,
|
fields,
|
||||||
added,
|
added,
|
||||||
capabilities,
|
capabilities,
|
||||||
@@ -123,6 +121,8 @@ function IndexerIndexRow(props: IndexerIndexRowProps) {
|
|||||||
enabled={enable}
|
enabled={enable}
|
||||||
redirect={redirect}
|
redirect={redirect}
|
||||||
status={status}
|
status={status}
|
||||||
|
longDateFormat={longDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
component={VirtualTableRowCell}
|
component={VirtualTableRowCell}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -7,3 +7,7 @@
|
|||||||
.statusIcon {
|
.statusIcon {
|
||||||
width: 20px !important;
|
width: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.indexerStatusTooltip {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
// This file is automatically generated.
|
// This file is automatically generated.
|
||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
|
'indexerStatusTooltip': string;
|
||||||
'status': string;
|
'status': string;
|
||||||
'statusIcon': string;
|
'statusIcon': string;
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
|
import VirtualTableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
|
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import { IndexerStatus } from 'Indexer/Indexer';
|
import { IndexerStatus } from 'Indexer/Indexer';
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
import translate from 'Utilities/String/translate';
|
||||||
|
import DisabledIndexerInfo from './DisabledIndexerInfo';
|
||||||
import styles from './IndexerStatusCell.css';
|
import styles from './IndexerStatusCell.css';
|
||||||
|
|
||||||
interface IndexerStatusCellProps {
|
interface IndexerStatusCellProps {
|
||||||
@@ -11,6 +13,8 @@ interface IndexerStatusCellProps {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
redirect: boolean;
|
redirect: boolean;
|
||||||
status: IndexerStatus;
|
status: IndexerStatus;
|
||||||
|
longDateFormat: string;
|
||||||
|
timeFormat: string;
|
||||||
component?: React.ElementType;
|
component?: React.ElementType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,6 +24,8 @@ function IndexerStatusCell(props: IndexerStatusCellProps) {
|
|||||||
enabled,
|
enabled,
|
||||||
redirect,
|
redirect,
|
||||||
status,
|
status,
|
||||||
|
longDateFormat,
|
||||||
|
timeFormat,
|
||||||
component: Component = VirtualTableRowCell,
|
component: Component = VirtualTableRowCell,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = props;
|
} = props;
|
||||||
@@ -41,13 +47,29 @@ function IndexerStatusCell(props: IndexerStatusCellProps) {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{status ? (
|
{status ? (
|
||||||
<Icon
|
<Popover
|
||||||
className={styles.statusIcon}
|
className={styles.indexerStatusTooltip}
|
||||||
kind={kinds.DANGER}
|
canFlip={true}
|
||||||
name={icons.WARNING}
|
anchor={
|
||||||
title={`Indexer is Disabled due to failures until ${formatDateTime(
|
<Icon
|
||||||
status.disabledTill
|
className={styles.statusIcon}
|
||||||
)}`}
|
kind={kinds.DANGER}
|
||||||
|
name={icons.WARNING}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title={translate('IndexerDisabled')}
|
||||||
|
body={
|
||||||
|
<div>
|
||||||
|
<DisabledIndexerInfo
|
||||||
|
mostRecentFailure={status.mostRecentFailure}
|
||||||
|
initialFailure={status.initialFailure}
|
||||||
|
disabledTill={status.disabledTill}
|
||||||
|
longDateFormat={longDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
position={tooltipPositions.BOTTOM}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Component>
|
</Component>
|
||||||
|
@@ -2,12 +2,16 @@ import { createSelector } from 'reselect';
|
|||||||
import Indexer from 'Indexer/Indexer';
|
import Indexer from 'Indexer/Indexer';
|
||||||
import createIndexerAppProfileSelector from 'Store/Selectors/createIndexerAppProfileSelector';
|
import createIndexerAppProfileSelector from 'Store/Selectors/createIndexerAppProfileSelector';
|
||||||
import createIndexerSelector from 'Store/Selectors/createIndexerSelector';
|
import createIndexerSelector from 'Store/Selectors/createIndexerSelector';
|
||||||
|
import createIndexerStatusSelector from 'Store/Selectors/createIndexerStatusSelector';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
|
||||||
function createIndexerIndexItemSelector(indexerId: number) {
|
function createIndexerIndexItemSelector(indexerId: number) {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createIndexerSelector(indexerId),
|
createIndexerSelector(indexerId),
|
||||||
createIndexerAppProfileSelector(indexerId),
|
createIndexerAppProfileSelector(indexerId),
|
||||||
(indexer: Indexer, appProfile) => {
|
createIndexerStatusSelector(indexerId),
|
||||||
|
createUISettingsSelector(),
|
||||||
|
(indexer: Indexer, appProfile, status, uiSettings) => {
|
||||||
// If a series is deleted this selector may fire before the parent
|
// If a series is deleted this selector may fire before the parent
|
||||||
// selectors, which will result in an undefined series, if that happens
|
// selectors, which will result in an undefined series, if that happens
|
||||||
// we want to return early here and again in the render function to avoid
|
// we want to return early here and again in the render function to avoid
|
||||||
@@ -20,6 +24,9 @@ function createIndexerIndexItemSelector(indexerId: number) {
|
|||||||
return {
|
return {
|
||||||
indexer,
|
indexer,
|
||||||
appProfile,
|
appProfile,
|
||||||
|
status,
|
||||||
|
longDateFormat: uiSettings.longDateFormat,
|
||||||
|
timeFormat: uiSettings.timeFormat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -2,6 +2,8 @@ import ModelBase from 'App/ModelBase';
|
|||||||
|
|
||||||
export interface IndexerStatus extends ModelBase {
|
export interface IndexerStatus extends ModelBase {
|
||||||
disabledTill: Date;
|
disabledTill: Date;
|
||||||
|
initialFailure: Date;
|
||||||
|
mostRecentFailure: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IndexerCategory extends ModelBase {
|
export interface IndexerCategory extends ModelBase {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
function createIndexerStatusSelector() {
|
function createIndexerStatusSelector(indexerId) {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { indexerId }) => indexerId,
|
|
||||||
(state) => state.indexerStatus.items,
|
(state) => state.indexerStatus.items,
|
||||||
(indexerId, indexerStatus) => {
|
(indexerStatus) => {
|
||||||
return _.find(indexerStatus, { indexerId });
|
return _.find(indexerStatus, { indexerId });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -116,6 +116,7 @@
|
|||||||
"Details": "Details",
|
"Details": "Details",
|
||||||
"DevelopmentSettings": "Development Settings",
|
"DevelopmentSettings": "Development Settings",
|
||||||
"Disabled": "Disabled",
|
"Disabled": "Disabled",
|
||||||
|
"DisabledUntil": "Disabled Until",
|
||||||
"Discord": "Discord",
|
"Discord": "Discord",
|
||||||
"Docker": "Docker",
|
"Docker": "Docker",
|
||||||
"Donations": "Donations",
|
"Donations": "Donations",
|
||||||
@@ -190,6 +191,7 @@
|
|||||||
"IndexerAlreadySetup": "At least one instance of indexer is already setup",
|
"IndexerAlreadySetup": "At least one instance of indexer is already setup",
|
||||||
"IndexerAuth": "Indexer Auth",
|
"IndexerAuth": "Indexer Auth",
|
||||||
"IndexerDetails": "Indexer Details",
|
"IndexerDetails": "Indexer Details",
|
||||||
|
"IndexerDisabled": "Indexer Disabled",
|
||||||
"IndexerFlags": "Indexer Flags",
|
"IndexerFlags": "Indexer Flags",
|
||||||
"IndexerHealthCheckNoIndexers": "No indexers enabled, Prowlarr will not return search results",
|
"IndexerHealthCheckNoIndexers": "No indexers enabled, Prowlarr will not return search results",
|
||||||
"IndexerInfo": "Indexer Info",
|
"IndexerInfo": "Indexer Info",
|
||||||
@@ -216,6 +218,7 @@
|
|||||||
"IndexerVipCheckExpiredClientMessage": "Indexer VIP benefits have expired: {0}",
|
"IndexerVipCheckExpiredClientMessage": "Indexer VIP benefits have expired: {0}",
|
||||||
"IndexerVipCheckExpiringClientMessage": "Indexer VIP benefits expiring soon: {0}",
|
"IndexerVipCheckExpiringClientMessage": "Indexer VIP benefits expiring soon: {0}",
|
||||||
"Info": "Info",
|
"Info": "Info",
|
||||||
|
"InitialFailure": "Initial Failure",
|
||||||
"InstanceName": "Instance Name",
|
"InstanceName": "Instance Name",
|
||||||
"InstanceNameHelpText": "Instance name in tab and for Syslog app name",
|
"InstanceNameHelpText": "Instance name in tab and for Syslog app name",
|
||||||
"InteractiveSearch": "Interactive Search",
|
"InteractiveSearch": "Interactive Search",
|
||||||
@@ -224,6 +227,7 @@
|
|||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"LastDuration": "Last Duration",
|
"LastDuration": "Last Duration",
|
||||||
"LastExecution": "Last Execution",
|
"LastExecution": "Last Execution",
|
||||||
|
"LastFailure": "Last Failure",
|
||||||
"LastWriteTime": "Last Write Time",
|
"LastWriteTime": "Last Write Time",
|
||||||
"LaunchBrowserHelpText": " Open a web browser and navigate to the Prowlarr homepage on app start.",
|
"LaunchBrowserHelpText": " Open a web browser and navigate to the Prowlarr homepage on app start.",
|
||||||
"Level": "Level",
|
"Level": "Level",
|
||||||
|
@@ -10,6 +10,8 @@ namespace Prowlarr.Api.V1.Indexers
|
|||||||
{
|
{
|
||||||
public int IndexerId { get; set; }
|
public int IndexerId { get; set; }
|
||||||
public DateTime? DisabledTill { get; set; }
|
public DateTime? DisabledTill { get; set; }
|
||||||
|
public DateTime? MostRecentFailure { get; set; }
|
||||||
|
public DateTime? InitialFailure { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IndexerStatusResourceMapper
|
public static class IndexerStatusResourceMapper
|
||||||
@@ -24,7 +26,9 @@ namespace Prowlarr.Api.V1.Indexers
|
|||||||
return new IndexerStatusResource
|
return new IndexerStatusResource
|
||||||
{
|
{
|
||||||
IndexerId = model.ProviderId,
|
IndexerId = model.ProviderId,
|
||||||
DisabledTill = model.DisabledTill
|
DisabledTill = model.DisabledTill,
|
||||||
|
MostRecentFailure = model.MostRecentFailure,
|
||||||
|
InitialFailure = model.InitialFailure,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user